面試整理(2)跨域:jsonp與CORS

問題:跨域有哪些方法?jsonp的原理是什麼?javascript

jsonp:html

先說jsonp,jsonp的主要原理是利用script標籤的src能夠跨域請求,聽說有src屬性的均可以跨域請求,但script標籤返回的會直接執行,因此都是用script請求。jsonp=json+padding,padding是指服務器返回數據時把數據用padding(函數)包裹了起來。也就是說,通常狀況下瀏覽器向服務器發送請求獲得的都是數據(文本,XML,JSON),可是當採用JSONP技術時候,瀏覽器向跨域服務器發送請求,獲得的是回調函數包住的JSON。此處JSON做爲參數傳入回調函數,而後再返回給瀏覽器。前端

這裏寫一個例子:其中後臺用的nodejsjava

前端代碼:index.htmlnode

<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.jsajax

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字段拿到回調函數名,而後用這個函數名包裹要返回的數據,造成一個字符串,發給前端json

例子在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兼容之前的版本。

相關文章
相關標籤/搜索