1. 程式人生 > >Ajax跨域原理及解決方案

Ajax跨域原理及解決方案

一次 變化 mes iframe 實現 type .ajax 一個 min

跨域請求的產生

跨域請求歸根結底是由於瀏覽器的“同源策略”引起的,同源策略指的是域名相同協議相同端口相同, 假設有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>
<html 
lang="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.comb.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跨域原理及解決方案