Ajax跨域原理及解決方案
跨域請求的產生
跨域請求歸根結底是由於瀏覽器的“同源策略”引起的,同源策略指的是域名相同
、協議相同
、端口相同
, 假設有http://www.a.com/test.html
,下面的示例 域名不同
http://demo.a.com/test1.html
協議不同
https://www.a.com/test2.html
端口號不同
http://www.a.com:8090/test3.html
實驗文件列表
先介紹一下文件列表:
a_ajax.html
是在域名a.ajax.com
下使用b_ajax.html
是在域名b.ajax.com
下使用ajax.html
是在域名www.ajax.com
ajax2.html
是在域名www.ajax2.com
下使用result.php
是公共使用返回文件
跨子域解決方案
如http://a.ajax.com/a_ajax.html
請求http://b.ajax.com/result.php
,通過iframe來加載http://b.ajax.com/b_ajax.html
,同時來提升域的級別,a_ajax.html示例代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>a_ajax</title> <script type="text/javascript"> document.domain = "ajax.com"; </script> </head> <body> <h1>a.ajax.com</h1> <iframe src="http://b.ajax.com/b_ajax.html"></iframe> </body> </html>
b.ajax.html示例代碼:
<!DOCTYPE html> <htmllang="en"> <head> <meta charset="UTF-8"> <title>b_ajax</title> <script src="jquery.min.js"></script> <script type="text/javascript"> document.domain = ‘ajax.com‘; function test() { $.ajax({ type: ‘GET‘, url: ‘http://b.ajax.com/result.php‘, data: { username: ‘qiaoweizhen‘ }, dataType: ‘json‘, success: function (response) { console.log(response); } }); } </script> </head> <body> <h1>b.ajax.com</h1> <a href="javascript:test();">Submit</a> </body> </html>
上述例子通過document.domain = ‘ajax.com‘
來將a.ajax.com
和b.ajax.com
提升到同一個根域ajax.com
下面來實現跨域。
jsonp解決方案
如http://www.ajax.com/ajax.html
請求http://www.ajax2.com/result.php
,示例代碼 ajax.html
function test() { $.ajax({ type: ‘GET‘, url: ‘http://www.ajax2.com/result.php‘, data: { username: ‘qiaoweizhen‘ }, dataType: ‘json‘, success: function (response) { console.log(response); } }); }
result.php文件示例代碼:
<?php $fp = fopen(‘log.txt‘, ‘w+‘); fwrite($fp, print_r($_REQUEST, true)); fclose($fp);
如果使用上述代碼去請求的話,先查看url,只有一個我們自己使用的參數username
:
然後查看報錯信息:
然後觀察服務器端沒有產生任何日誌文件,則說明在瀏覽器端就被禁用掉了。如果將請求改成jsonp
代碼如下:
function test() { $.ajax({ type: ‘GET‘, url: ‘http://www.ajax2.com/result.php‘, data: { username: ‘qiaoweizhen‘ }, dataType: ‘jsonp‘, success: function (response) { console.log(response); } }); }
發起請求的話,就會發現url有所變化,使用jquery會默認增加callback
和_
參數:
查看控制臺輸出:
而查看服務端日誌,如下:
說明兩者之間的通信是沒有問題,就需要將處理的結果返回給客戶端,可以修改result.php
代碼,示例如下:
<?php $fp = fopen(‘log.txt‘, ‘w+‘); fwrite($fp, print_r($_REQUEST, true)); fclose($fp); $callback = $_REQUEST[‘callback‘]; $username = $_REQUEST[‘username‘]; $response = [ ‘status‘ => 1, ‘message‘ => ‘ok‘, ‘data‘ => [ ‘username‘ => $username ] ]; echo $callback . ‘(‘ . json_encode($response) . ‘)‘;
ok,上面的實例就完成了通過jsonp進行跨域請求的操作,有以下幾點需要說明:
jsonp
只支持GET
請求,這是因為jsonp請求,歸根結底是通過動態從服務端加載一段JavaScript腳本來在客戶端執行,返回實例:jQuery183014317321930723415_1483925970177({"status":1,"message":"ok","data":{"username":"qiaoweizhen"}})
jsonp
中的callback
是可以自定義的,只需要和dataType
同級別參數jsonpCallback
,如jsonpCallback: ‘myCallback‘
,在定義一個myCallback
函數就可以了- 可不可以在
data
中傳入callback
參數,是可以的,但是容易混淆,而且服務端接收參數的時候只能接收到一個callback
參數,不建議這麽做
CORS(跨域資源共享)解決方案
上面通過jsonp
來實現了簡單的跨域請求,但是會面臨以下幾個問題:
- 請求來源是否合法
- 請求數據大小有限制,因為是
GET
請求 jsonp
不提供錯誤處理機制,即動態加載的返回代碼是有問題的話,沒有相應的處理機制 解決上面最好的方式是通過CORS
,經常使用服務端兩個參數Access-Control-Allow-Origin(配置域名)
,Access-Control-Request-Method(配置方法)
。如http://www.ajax.com/ajax.html
請求http://www.ajax2.com/result.php
,ajax.html示例代碼如下:
function test() { $.ajax({ type: ‘GET‘, url: ‘http://www.ajax2.com/result.php‘, data: { username: ‘qiaoweizhen‘ }, dataType: ‘json‘, success: function (response) { console.log(response); } }); }
服務端通過設置返回頭信息,來進行一定的限制,服務端result.php示例代碼如下:
<?php header(‘Access-Control-Allow-Origin:*‘);
這樣子就允許所有域名進行ajax跨域請求,如果需要限制,示例代碼如下:
<?php $allowOrigins = [ ‘http://www.ajax.com‘, ‘http://a.ajax.com‘ ]; if (in_array($_SERVER[‘HTTP_ORIGIN‘], $allowOrigins)) { header(‘Access-Control-Allow-Origin:‘ . $_SERVER[‘HTTP_ORIGIN‘]); echo json_encode([ ‘status‘ => 1, ‘message‘ => ‘ok‘ ]); }
上面的是通過簡單請求,如果非簡單請求,服務器會先通過預檢機制來進行限制,ajax.html示例代碼:
function test() { $.ajax({ type: ‘PUT‘, url: ‘http://www.ajax2.com/result.php‘, data: { username: ‘qiaoweizhen‘ }, dataType: ‘json‘, success: function (response) { console.log(response); } }); }
如果發送了一個PUT
請求,查看請求url,發現是Request Method:OPTIONS
而不是Request Method:PUT
,這是因為瀏覽器和服務器之間進行了一次預檢通信機制:
如果服務端設置了,可以使用PUT
請求,服務端result.php
代碼如下:
$allowOrigins = [ ‘http://www.ajax.com‘, ‘http://a.ajax.com‘ ]; header(‘Access-Control-Allow-Origin:*‘); header(‘Access-Control-Allow-Methods:PUT‘);
那麽就會發生兩次請求,一次是預檢機制的OPTIONS
請求,一次是真正的PUT
請求
Ajax跨域原理及解決方案