jsonp 跨域原理分析

本篇文章借鑑自 博客園文章

原文地址

AJAX即「Asynchronous Javascript And XML」(異步JavaScript和XML),是指一種建立交互式網頁應用的網頁開發技術。javascript

AJAX 的出現使得網頁能夠經過在後臺與服務器進行少許數據交換,實現網頁的局部刷新。php

可是出於安全的考慮,ajax不容許跨域通訊(瀏覽器同源策略)。
若是嘗試從不一樣的域請求數據,就會出現錯誤(跨域錯誤)。
在實際開發中,每每須要進行跨於請求,這時要怎麼辦呢?css

關於ajax跨域請求?

一、Ajax爲何不能跨域?究竟是卡在哪一個環節了?。 (請求成功了,但客戶端瀏覽器拿不到請求結果)html

Ajax其實就是向服務器發送一個GET或POST請求,而後取得服務器響應結果,返回客戶端。

理論上這是沒有任何問題的,然而普通ajax跨域請求,在服務器端不會有任何問題,只是服務端響應數據返回給瀏覽器的時候,

瀏覽器根據響應頭的Access-Control-Allow-Origin字段的值來判斷是否有權限獲取數據,

通常狀況下,服務器端若是沒有在這個字段作特殊處理的話,跨域是沒有權限訪問的,因此響應數據被瀏覽器給攔截了,

因此在ajax回調函數裏是獲取不到數據的。因此如今ajax跨域的問題能夠轉化爲數據怎麼拿回客戶端的問題。

二、 html的script標籤,img標籤,iframe標籤,能夠請求第三方的資源(不受同源策略影響)前端

web頁面能夠加載放在任意站點的js、css、圖片等資源,不會受到"跨域"的影響。

這個時候,咱們會想到:既然咱們能夠調用第三方站點的js,那麼若是咱們將數據放到第三方站點的js中不就能夠將數據帶到客戶端了嗎?

JSONP

一、什麼是JSONP?html5

JSONP(JSON with Padding(填充))是JSON的一種「使用模式」,可用於解決主流瀏覽器的跨域數據訪問的問題。

其核心思想是利用JS標籤裏面的跨域特性進行跨域數據訪問,
在JS標籤裏面存在的是一個跨域的URL,實際執行的時候經過這個URL得到一段字符串,
這段返回的字符串必須是一個合法的JS調用,經過EVAL這個字符串來完成對得到的數據的處理。

即: <script src='url'></script>

JSONP是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,
經過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。

二、JSONP的粗糙實現java

下面咱們經過一個例子來講明一下JSONP是如何實現ajax跨域請求的。mysql

html 代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>jsonp</div>
</body>
<script>
    function getremotedata(data) {
        console.log(data);
    }
    var div = document.getElementsByTagName('div');

    div[0].onclick = function(){
        var url = "./getdata.js";
        var script = document.createElement('script');
        script.setAttribute('src', url);
        document.getElementsByTagName('head')[0].appendChild(script);
    };
</script>
</html>

遠程的getdata.js

getremotedata({
    code:0,
    result:'success'
});

獲得的結果:
動態添加的script標籤
獲得結果jquery

jsonp前端及後臺php的寫法

html代碼仍是和上面同樣,只要改變 url就能夠了
url= 'localhsot:8080/search.php?callback=getremotedata' git

後臺 php 代碼
<?php
    $callback = $_GET['callback'];

    if($callback == 'getremotedata' ){
        echo $_GET['$callback']).'('. json_encode({code:0,msg:"success"}) .')';
    }
    ?>

看到這裏清楚了吧,就是第三方站點生成一個對回調函數的調用,傳入查詢結果,
而後經過 <script> 加載到客戶端執行

下圖是 jsonp請求的流程圖
jsonp請求的流程圖

jquery 封裝在 ajax方法 裏面的jsonp

jquery 是如何把 jsonp 封裝到ajax裏面的?

<script type="text/javascript">
    function GetAjaxData() {
        $.ajax({
            type: "get",
            async: false,
            url: "http://localhost:8080/getdata.php",
            dataType: "jsonp",
            jsonp: "callback",//傳遞給請求處理程序或頁面的,標識jsonp回調函數名(通常爲:callback)
            jsonpCallback: "GetData",//callback的function名稱
            success: function (data) {
                console.log(data);
            },
            error: function () {
                alert('fail');
            }
        });
    }
</script>

通常狀況下jqury 生成的訪問 遠程站點的 url
默認狀況下:(我所在的實際項目中的使用)

http://web.k3k.net/haila3/pt/tp/index.php/Home/User/getusergoto/?callback=jQuery191028614189839964865_1497261919344&token=420171c8-031a58667e64&_=1497261919346

上述代碼請求生成的url(設置 jsonpCallback的值爲 GetData

http://web.k3k.net/haila3/pt/tp/index.php/Home/User/getusergoto/?callback=GetData&token=420171c8-00b8-031a58667e64&_=1497261919346

最後 一個 _=1497261919346 k v 是爲了防止瀏覽器緩存,而由 jquery 自動增長上的。

因此至關於 在 前端文件中引入了 一個這樣的js文件

<script src="http://web.k3k.net/haila3/pt/tp/index.php/Home/User/getusergoto/?callback=GetData&token=420171c8-00b8-031a58667e64&_=1497261919346"></script>

這裏有2個重要的參數

jsonp
在一個jsonp請求中重寫回調函數的名字。這個值用來替代在"callback=?"這種GET或POST請求中URL參數裏的"callback"部分,
好比{jsonp:'onJsonPLoad'}會致使將"onJsonPLoad=?"傳給服務器。

jsonpCallback
爲jsonp請求指定一個回調函數名。這個值將用來取代jQuery自動生成的隨機函數名。
這主要用來讓jQuery生成一個獨特的函數名,這樣管理請求更容易,也能方便地提供回調函數和錯誤處理。

你也能夠在想讓瀏覽器緩存GET請求的時候,指定這個回調函數名。

從jQuery 1.5開始,你也可使用一個函數做爲該參數設置,在這種狀況下,該函數的返回值就是jsonpCallback的結果。


經過一開始 jsonp 原理的分析,能夠得出:

當咱們正常地請求一個JSON數據的時候,服務端返回的是一串JSON類型的數據。
而咱們使用JSONP模式來請求數據的時候,服務端返回的是一段可執行的JavaScript代碼

因此咱們可見服務器代碼最後一行

echo $_GET['$callback']).'('. json_encode({code:0,msg:"success"}) .')';

就是執行的 getdata,而後把數據經過回調的方式傳遞過去

OK,就是整個流程就是:

客戶端發送一個請求,規定一個可執行的函數名
(這裏就是jQuery作了封裝的處理,自動幫你生成回調函數並把數據取出來供success屬性方法來調用,不是傳遞的一個回調句柄),
服務端接受了這個 getdata 函數名,而後把數據經過實參的形式發送出去


以上是 jquery 封裝的 ajax方法裏面的 jsonp 請求,說來講去,本身都好像忘記了普通的 ajax請求

js原生 ajax 請求
//1.建立對象
        var ajax = '';

        if(window.XMLHttpRequest){
            ajax = new XMLHttpRequest();    /* 現代瀏覽器 */
        }else if(window.ActiveXObject){
            ajax = new ActiveXObject("Microsoft.XMLHTTP");  /* 萬惡的ie瀏覽器 */
        }

        //2.建立請求

        //get請求方法(拼接url參數)
//      var url="login.php?name="+name+"&password="+pass;
//      ajax.open("GET",url,true);

        //post請求
        ajax.open("POST","login.php",true);
        ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        var data="name="+name+"&password="+pass;

        //3.發送請求
//      ajax.send();        //get 方式發送請求
        ajax.send(data);    //post 方式發送請求

        //4.捕獲請求狀態、onreadystatechange表示當前請求狀態

        ajax.onreadystatechange=function(){
            //5.判斷請求狀態
            if(ajax.readyState==4){
                //6.判斷請求結果
                if(ajax.status==200){
                    //請求成功將結果 responseText 放入回調函數中
                    succ(ajax.responseText);
                }
            }
        }

注意

經過檢測window對象是否有XMLHttpRequest屬性來肯定瀏覽器是否支持標準的XMLHttpRequest。
注意,不要根據瀏覽器的navigator.userAgent來檢測瀏覽器是否支持某個JavaScript特性,一是由於這個字符串自己能夠僞造,二是經過IE版本判斷JavaScript特性將很是複雜。

當建立了XMLHttpRequest對象後,要先設置onreadystatechange的回調函數。在回調函數中,一般咱們只需經過readyState === 4判斷請求是否完成,
若是已完成,再根據status === 200判斷是不是一個成功的響應。

XMLHttpRequest對象的open()方法有3個參數,
第一個參數指定是GET仍是POST,
第二個參數指定URL地址,
第三個參數指定是否使用異步,默認是true,因此不用寫。

注意,千萬不要把第三個參數指定爲false,不然瀏覽器將中止響應,直到AJAX請求完成。
若是這個請求耗時10秒,那麼10秒內你會發現瀏覽器處於「假死」狀態。

最後調用send()方法才真正發送請求。
GET請求不須要參數,
POST請求須要把body部分以字符串或者FormData對象傳進去。

jquery實現普通ajax

<script type="text/javascript">
    $("#btn").on("click",function(){
        var name=$("#name").val();
        var pass=$("#password").val();

        $.ajax({
            type:"post",
            url:"login&jq.php",
            async:true,        //異步簡寫
            dataType:"json",   //轉化爲json類型
            data:{
                name:name,
                password:pass,
            },
            success:function(data){
                console.log(data);
            },
            error:function(data){
                alert(data);
            }
        });
    })
    </script>

後臺 php 代碼

<?php
    include_once "common.php";
    $name=$_POST["name"];
    $password=$_POST["password"];

    $sql="select*from user where name='$name' and password= '$password'";

    $result=mysql_query($sql);

    if(mysql_num_rows($result)==1){
        $row = mysql_fetch_assoc($result);
        //只能傳一個json
        echo json_encode($row);
    }else{
        //只能用json
        echo '{"msg":"輸入有誤"}';
    }

?>

固然實現跨域的方法還有不少,html5規範 的 CORS(全稱Cross-Origin Resource Sharing),是HTML5規範定義的如何跨域訪問資源。

瞭解CORS前,咱們先搞明白概念:
Origin表示本域,也就是瀏覽器當前頁面的域。當JavaScript向外域(如sina.com)
發起請求後,瀏覽器收到響應後,首先檢查Access-Control-Allow-Origin是否包含本域,
若是是,則這次跨域請求成功,若是不是,則請求失敗,JavaScript將沒法獲取到響應的任何數據。

假設本域是my.com,外域是sina.com,只要響應頭Access-Control-Allow-Origin爲http://my.com,或者是*,本次請求就能夠成功。

可見,跨域可否成功,取決於對方服務器是否願意給你設置一個正確的Access-Control-Allow-Origin,決定權始終在對方手中。

總結

一、ajax和jsonp這兩種技術在調用方式上「看起來」很像,目的也同樣,都是請求一個url,而後把服務器返回的數據進行處理,所以jquery和ext等框架都把jsonp做爲ajax的一種形式進行了封裝;

二、ajax和jsonp其實本質上是不一樣的東西。ajax的核心是經過XMLHttpRequest獲取非本頁內容,而jsonp的核心則是經過HTTP來動態添加 <script> 標籤來調用服務器提供的js腳本。

三、其實ajax與jsonp的區別不在因而否跨域,ajax經過服務端代理(CORS)同樣能夠實現跨域,jsonp自己也不排斥同域的數據的獲取。

四、jsonp是一種方式或者說非強制性協議,如同ajax同樣,它也不必定非要用json格式來傳遞數據,若是你願意,字符串都行,只不過這樣不利於用jsonp提供公開服務。

五、jsonp整個過程當中,本地站點一直處於主動的地位,主動的發送請求,主動的加載遠程js.而第三方站點則處於被動的響應。

josnp 優缺點分析: 借鑑自w3cfun

優勢:

1.1它不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制,JSONP能夠跨越同源策略;
    
1.2它的兼容性更好,在更加古老的瀏覽器中均可以運行,不須要XMLHttpRequest或ActiveX的支持;
    
1.3在請求完畢後能夠經過調用callback的方式回傳結果。
將回調方法的權限給了調用方。這個就至關於將controller層和view層終於分開了。
我提供的jsonp服務只提供純服務的數據,至於提供服務以 後的頁面渲染和後續view操做都由調用者來本身定義就行了。
若是有兩個頁面須要渲染同一份數據,大家只須要有不一樣的渲染邏輯就能夠了,
邏輯均可以使用同 一個jsonp服務。

缺點

2.1它只支持GET請求而不支持POST等其它類型的HTTP請求

2.2它只支持跨域HTTP請求這種狀況,不能解決不一樣域的兩個頁面之間如何進行JavaScript調用的問題。

2.3 jsonp在調用失敗的時候不會返回各類HTTP狀態碼。

2.4缺點是安全性。萬一假如提供jsonp的服務存在頁面注入漏洞,即它返回的javascript的內容被人控制的。
那麼結果是什麼?全部調用這個 jsonp的網站都會存在漏洞。
因而沒法把危險控制在一個域名下…因此在使用jsonp的時候必需要保證使用的jsonp服務必須是安全可信的。
相關文章
相關標籤/搜索