問題:跨域有哪些方法?jsonp的原理是什麼? jsonp: 先說jsonp,jsonp的主要原理是利用script標簽的src可以跨域請求,據說有src屬性的都可以跨域請求,但script標簽返回的會直接執行,所以都是用script請求。jsonp=json+padding,padding是指服務 ...
問題:跨域有哪些方法?jsonp的原理是什麼?
jsonp:
先說jsonp,jsonp的主要原理是利用script標簽的src可以跨域請求,據說有src屬性的都可以跨域請求,但script標簽返回的會直接執行,所以都是用script請求。jsonp=json+padding,padding是指伺服器返回數據時把數據用padding(函數)包裹了起來。也就是說,一般情況下瀏覽器向伺服器發送請求得到的都是數據(文本,XML,JSON),但是當採用JSONP技術時候,瀏覽器向跨域伺服器發送請求,得到的是回調函數包住的JSON。此處JSON作為參數傳入回調函數,然後再返回給瀏覽器。
這裡寫一個例子:其中後臺用的nodejs
前端代碼:index.html
<html> <head></head> <body> <div id="myDiv"></div> </body> <script type="text/javascript"> function jsonpcallback(data){ console.log(data.text) } var s = document.createElement('script'); s.src="http://localhost:3000?callback=jsonpcallback"; document.body.appendChild(s); </script> </html>
後臺代碼:server.js
var http = require('http') var url = require('url') http.createServer(function(req,res){ var param = url.parse(req.url, true).query var callback = param.callback var data = { text: "hello world" } var resStr = callback+"("+JSON.stringify(data)+")" res.write(resStr) res.end() }).listen(3000)
上面的例子中,index.html中定義了一個jsonpcallback函數,並且把這個函數名通作為callback欄位的值以get的請求(src的請求只能是get)發送給服務端。服務端接收到之後通過callback欄位拿到回調函數名,然後用這個函數名包裹要返回的數據,形成一個字元串,發給前端
例子在node環境下運行:node server.js,然後再運行index.html就可以看到前端跨域請求的結果。例子中由於index.html是本地的,所以跟伺服器不同源,普通的請求會被拒絕。
服務端寫的時候要註意,返回的是一個很像函數調用的字元串,它不並不是後臺對callback函數的調用,所以要把返回的對象用JSON.stringify轉化一下,再加上字元串的兩個括弧
CORS:
jsonp說完之後,再說一個常用的跨域方法CORS。主要參考阮一峰大神的資料:http://www.ruanyifeng.com/blog/2016/04/cors.html
CORS分簡單請求和非簡單請求,同時滿足下麵兩個條件的就是簡單請求,否則就是非簡單請求
簡單請求:請求頭通過Origin
欄位用來說明,本次請求來自哪個源(協議 + 功能變數名稱 + 埠)。伺服器根據這個值,決定是否同意這次請求。
我在實際測試時先不使用關於cookie的一些可選欄位,就簡單的在前端設置Origin在後端設置Access-Control-Allow-Origin這種必須的欄位來測試。
發現前端的ajax設置Origin欄位會報錯-》Refused to set unsafe header "Origin"
雖然不影響請求的發送,但設置的Origin欄位是無效的,不管我設置什麼或者不設置這個欄位時查看Origin欄位時它的值都是null,網上查了之後在stackoverflow上有人說那個Origin欄位本來就應該是瀏覽器來設置的,自己設置不安全。
猜想只要瀏覽器發現請求的目標不是同源的,就會自己給報文加Origin欄位。所以只要在後端設置Access-Control-Allow-Origin欄位為*(表示接受任意Origin的請求)就可以了。
前端代碼:簡單的ajax即可
服務端代碼:server.js
var http = require('http') http.createServer(function(req,res){ res.writeHead(200, { 'Access-Control-Allow-Origin' : '*' }); res.write("hello world!") res.end() }).listen(3000)
非簡單請求:除去可選欄位,在非簡單請求中服務端還要指定Access-Control-Request-Method來表示自己接受的請求的方法,把之前的ajax中的方法從get改為put
前端代碼:index.html
<html> <head></head> <body> <div id="myDiv"></div> </body> <script type="text/javascript"> var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP") xmlhttp.open("PUT","http://localhost:3000/",true) xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("myDiv").innerHTML=xmlhttp.responseText; } } xmlhttp.send() </script> </html>
如果後臺沒有在Access-Control-Request-Method中添加put方法就會報錯-》Failed to load http://localhost:3000/: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
後端代碼:server.js
var http = require('http') var url = require('url') http.createServer(function(req,res){ res.writeHead(200, { 'Access-Control-Allow-Origin' : '*', 'Access-Control-Allow-Methods' : 'GET, POST, PUT' }); res.write("hello world!") res.end() }).listen(3000)
添加了Access-Control-Request-Method:“PUT”後請求成功。
非簡單方法瀏覽器會先發起一個method為OPTION的“預檢”來跟伺服器驗證自己的請求是不是被允許,如果伺服器回的頭中沒有請求的方法或者參數,那麼瀏覽器就知道不允許,將不發送ajax請求。如果有,瀏覽器才會再發送那個ajax請求。
“預檢”請求:
CORS和jsonp的區別主要在於:jsonp只支持get請求,而CORS支持所有類型的請求,只不過CORS要求至少IE10,jsonp相容以前的版本。