利用 JSONP 實現跨域調用
說道跨域調用,可能大家首先想到的或者聽說過的就是 JSONP 了。
1.1 什么是JSONP
JSONP 是 JSON 的一種使用模式,可以解決主流瀏覽器的跨域數據訪問問題。其原理是根據 XmlHttpRequest 對象受到同源策略的影響,而 <script> 標簽元素卻不受同源策略影響,可以加載跨域服務器上的腳本,網頁可以從其他來源動態產生 JSON 資料。用 JSONP 獲取的不是 JSON 數據,而是可以直接運行的 JavaScript 語句。
1.2 使用 jQuery 集成的 $.ajax 實現 JSONP 跨域調用
下面的例子,我們將 服務器 3000 上的請求頁面的 JavaScript 代碼為:
// 回調函數 function jsonpCallback(data) { console.log("jsonpCallback: " + data.name) } $("#submit").click(function() { var data = { name: $("#name").val(), id: $("#id").val() }; $.ajax({ url: 'http://localhost:3001/ajax/deal', data: data, dataType: 'jsonp', cache: false, timeout: 5000, // jsonp 字段含義為服務器通過什么字段獲取回調函數的名稱 jsonp: 'callback', // 聲明本地回調函數的名稱,jquery 默認隨機生成一個函數名稱 jsonpCallback: 'jsonpCallback', success: function(data) { console.log("ajax success callback: " + data.name) }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus + ' ' + errorThrown); } }); });
服務器 3001 上對應的處理函數為:
1 app.get('/ajax/deal', function(req, res) { 2 console.log("server accept: ", req.query.name, req.query.id) 3 var data = "{" + "name:'" + req.query.name + " - server 3001 process'," + "id:'" + req.query.id + " - server 3001 process'" + "}" 4 var callback = req.query.callback //獲得請求端回調函數 5 var jsonp = callback + '(' + data + ')' 6 console.log(jsonp) 7 res.send(jsonp) 8 res.end() 9 })
這里一定要注意 data 中字符串拼接,不能直接將 JSON 格式的 data 直接傳給回調函數,否則會發生編譯錯誤: parsererror Error: jsonpCallback was not called。
1.3 使用 <script> 標簽原生實現 JSONP
經過上面的事件,你是不是覺得 JSONP 的實現和 Ajax 大同小異?
其實,由於實現的原理不同,由 JSONP 實現的跨域調用不是通過 XmlHttpRequset 對象,而是通過 script 標簽,所以在實現原理上,JSONP 和 Ajax 已經一點關系都沒有了。看上去形式相似只是由於 jQuery 對 JSONP 做了封裝和轉換。
比如在上面的例子中,我們假設要傳輸的數據 data 格式如下:
{ name: "chiaki", id": "3001" }
那么數據是如何傳輸的呢?HTTP 請求頭的第一行如下:
GET /ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1
可見,即使形式上是用 POST 傳輸一個 JSON 格式的數據,其實發送請求時還是轉換成 GET 請求。
其實如果理解 JSONP 的原理的話就不難理解為什么只能使用 GET 請求方法了。由於是通過 script 標簽進行請求,所以上述傳輸過程根本上是以下的形式:
<script src = 'http://localhost:3001/ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032'></script>
這樣從服務器返回的代碼就可以直接在這個 script 標簽中運行了。下面我們自己實現一個 JSONP:
-
服務器 3000請求頁面的 JavaScript 代碼中,只有回調函數 jsonpCallback:
-
function jsonpCallback(data) { console.log("jsonpCallback: "+data.name) }
服務器 3000請求頁面還包含一個
script標簽: -
<script src = 'http://localhost:3001/jsonServerResponse?jsonp=jsonpCallback'></script>
服務器 3001上對應的處理函數:
1 app.get('/jsonServerResponse', function(req, res) { 2 var cb = req.query.jsonp //這里得到請求頁面的回調函數 3 console.log(cb)
//思考一下為什么這里要這樣寫 4 var data = 'var data = {' + 'name: $("#name").val() + " - server 3001 jsonp process",' + 'id: $("#id").val() + " - server 3001 jsonp process"' + '};' 5 var debug = 'console.log(data);' //打印var data=""; 6 var callback = '$("#submit").click(function() {' + data + cb + '(data);' + debug + '});' 7 res.send(callback) //返回的是一個點擊按鈕的事件 8 res.end() 9 })與上面一樣,我們在所獲取的參數后面加上 “ - server 3001 jsonp process” 代表服務器對數據的操作。從代碼中我么可以看到,處理函數除了根據參數做相應的處理,更多的也是進行字符串的拼接。
-
2.4 JSONP 總結
至此,我們了解了 JSONP 的原理以及實現方式,它幫我們實現前端跨域請求,但是在實踐的過程中,我們還是可以發現它的不足:
- 只能使用 GET 方法發起請求,這是由於
script標簽自身的限制決定的。 - 不能很好的發現錯誤,並進行處理。與 Ajax 對比,由於不是通過 XmlHttpRequest 進行傳輸,所以不能注冊 success、 error 等事件監聽函數。
-
使用 CORS 實現跨域調用
3.1 什么是 CORS?
Cross-Origin Resource Sharing(CORS)跨域資源共享是一份瀏覽器技術的規范,提供了 Web 服務從不同域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,是 JSONP 模式的現代版。與 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以讓網頁設計師用一般的 XMLHttpRequest,這種方式的錯誤處理比 JSONP 要來的好。另一方面,JSONP 可以在不支持 CORS 的老舊瀏覽器上運作。現代的瀏覽器都支持 CORS。
-
3.2 CORS 的實現
還是以 服務器 3000 上的請求頁面向 服務器 3001 發送請求為例。
-
服務器 3000 上的請求頁面 JavaScript 不變,服務器 3001上對應的處理函數:
-
1 app.post('/cors', function(req, res) { 2 res.header("Access-Control-Allow-Origin", "*"); //設置請求來源不受限制 3 res.header("Access-Control-Allow-Headers", "X-Requested-With"); 4 res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); //請求方式 5 res.header("X-Powered-By", ' 3.2.1') 6 res.header("Content-Type", "application/json;charset=utf-8"); 7 var data = { 8 name: req.body.name + ' - server 3001 cors process', 9 id: req.body.id + ' - server 3001 cors process' 10 } 11 console.log(data) 12 res.send(data) 13 res.end() 14 })
3.3 CORS 中屬性的分析
-
-
Access-Control-Allow-Origin
The origin parameter specifies a URI that may access the resource. The browser must enforce this. For requests without credentials, the server may specify “*” as a wildcard, thereby allowing any origin to access the resource.
-
Access-Control-Allow-Methods
Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request. The conditions under which a request is preflighted are discussed above.
-
Access-Control-Allow-Headers
Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
3.4 CORS 與 JSONP 的對比
- CORS 除了 GET 方法外,也支持其它的 HTTP 請求方法如 POST、 PUT 等。
- CORS 可以使用 XmlHttpRequest 進行傳輸,所以它的錯誤處理方式比 JSONP 好。
- JSONP 可以在不支持 CORS 的老舊瀏覽器上運作。
-
-
- 只能使用 GET 方法發起請求,這是由於
