淺談Ajax跨域及其JSONP簡單實現
淺談跨域
跨域是指跨域名的訪問,有三種情況:
域名不同的跨域。
域名相同、端口不同的跨域。
二級域名不同的跨域。
跨域問題來源于JavaScript的"同源策略",即只有?協議+主機名+端口號 (如存在)相同,則允許相互訪問。也就是說JavaScript只能訪問和操作自己域下的資源,不能訪問和操作其他域下的資源。跨域問題是針對JS和AJAX的,html本身沒有跨域問題。
例如下面網站:
http://www.abc.com/a/b 調用 http://www.abc.com/d/c(非跨域)
http://www.abc.com/a/b 調用 http://www.def.com/a/b (跨域:域名不一致)
http://www.abc.com:8080/a/b 調用 http://www.abc.com:8081/d/c (跨域:端口不一致)
http://www.abc.com/a/b 調用 https://www.abc.com/d/c (跨域:協議不同)
值得一提的是:localhost和127.0.0.1雖然都指向本機,但也屬于跨域。
同源策略
同源策略指的是:協議+域名+端口三者皆相同,可以視為在同一個域,否則為不同域。同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。
作用是一個用于隔離潛在惡意文件的重要安全機制。
所限制的跨域交互包括:
Cookie、LocalStorage、IndexdDB 等存儲內容;
DOM 節點;
Ajax 請求。
跨域的常見解決方法
jsonp:只支持 GET,不支持 POST 請求,不安全 XSS
cors:需要后臺配合進行相關的設置
postMessage:配合使用 iframe,需要兼容 IE6、7、8、9
document.domain:僅限于同一域名下的子域
websocket:需要后臺配合修改協議,不兼容,需要使用 socket.io
proxy:使用代理去避開跨域請求,需要修改 nginx、apache 等的配置(常規解決方案)
Ajax 其實就是向服務器發送一個 GET 或 POST 請求,然后取得服務器響應結果,返回客戶端。Ajax 跨域請求,在服務器端不會有任何問題,只是服務端響應數據返回給瀏覽器的時候,瀏覽器根據響應頭的Access-Control-Allow-Origin字段的值來判斷是否有權限獲取數據。
因此,服務端如果沒有設置跨域字段設置,跨域是沒有權限訪問,數據被瀏覽器給攔截了。
所以ajax本身是不可以跨域的,通過產生一個script標簽來實現跨域。因為script標簽的src屬性是沒有跨域的限制的。
其實設置了dataType: 'jsonp'后,$.ajax方法就和ajax XmlHttpRequest沒什么關系了,取而代之的則是JSONP協議。JSONP是一個非官方的協議,它允許在服務器端集成Script tags返回至客戶端,通過javascript callback的形式實現跨域訪問。
通俗點來說,JSONP(JSON with Padding(填充))是 JSON 的一種“使用模式”,本質不是 Ajax 請求,是 script 標簽請求。
舉個栗子
這里我們使用JSONP 實現一下:
class Jsonp { constructor(req) { this.url = req.url; this.callbackName = req.callbackName; } create() { const script = document.createElement("script"); const url = `${this.url}?callback=${this.callbackName}`; script.src = url; document.getElementsByTagName("head")[0].appendChild(script); } } new Jsonp({ url: "http://127.0.0.1:8000/", callbackName: "getMsg" }).create(); function getMsg(data) { data = JSON.parse(data); console.log(`My name is ${data.name}, and ${data.age} years old.`); }
服務端:
const http = require("http"); const querystring = require("querystring"); const server = http.createServer((req, res) => { const url = req.url; const query = querystring.parse(url.split("?")[1]); const { callback } = query; const data = { name: "yunqinanhai", age: "18" }; res.end(`${callback}('${JSON.stringify(data)}')`); }); server.listen(8000);
前端利用?http-server -p 8001 .,開啟一個服務,然后 Node 也開啟一個端口為 8000 的服務,運行:
My name is yunqinanhai, and 18 years old.
一個 JSONP 的步驟實質
客戶端發送 script 請求,參數中帶著處理返回數據的回調函數的名字 (通常是 callback),如請求 script 的 url 是:
http://127.0.0.1:8000/?callback=getMsg
服務端收到請求,以回調函數名和返回數據組成立即執行函數的字符串,比如:其中 callback 的值是客戶端發來的回調函數的名字,假設回調函數的名字是 getMsg,返回腳本的內容就是:
getMsg("{name: 'yunqinanhai', age: '18'}");
客戶端收到 JavaScript 腳本內容后,立即執行腳本,這樣就實現了獲取跨域服務器數據的目的。
很明顯,由于 JSONP 技術本質上利用了 script 腳本請求,所以只能實現 GET 跨域請求,這也是 JSONP 跨域的最大限制。
由于 server 產生的響應為 json 數據的包裝(故稱之為 jsonp,即 json padding),
形如:getMsg("{name: 'yunqinanhai', age: '18'}")
總結
JSONP?請求本質上是利用了 “Ajax 請求會受到同源策略限制,而 script 標簽請求不會” 這一點來繞過同源策略。
Ajax 域名注冊服務
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。