關於jsonp,cors跨域的那些事

一直覺得之前對jsonp道理和實現已經掌握了(src不受同源策略的限制,經過新建scrpit來加入dom,並綁定回調函數來實現跨域),可是最近被問到的時候仍是有種只知其一;不知其二,說不全,今天來總結一下jsonp的一些難點(因爲jq對jsonp的封裝致使了不少不一致的地方)
有不少內容摘參考了https://mp.weixin.qq.com/s/9I...ajax

json

JSONP 是 JSON 的一種使用模式,能夠解決主流瀏覽器的跨域數據訪問問題。其原理是根據 XmlHttpRequest 對象受到同源策略的影響,而 <script> 標籤元素卻不受同源策略影響,能夠加載跨域服務器上的腳本,網頁能夠從其餘來源動態產生 JSON 資料。用 JSONP 獲取的不是 JSON 數據,而是能夠直接運行的 JavaScript 語句。json

jsonp源碼

jsonp({
    url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',
    type: 'get',
    data:{
        wd: 'jsonp'
    },
    callback: 'cb',
    success: function (data) { 
        console.log(data) 
    }
});

function jsonp (options) {
    let url = options.url
    let data = options.data
    
    let oBody = document.getElementsByTagName('body')[0]
    let oScript = document.createElement('script')
    
    let callbackName = 'cb' + (~~(Math.random()*0xffffff)).toString(16)
    window[callbackName] = function (result) {
        options.success(result)        
    }
    data[options.callback] = callbackName
    
    oScript
        .setAttribute('src', url + '?' + format(data))
    oBody.append(oScript)
}
function format(data) {
    let str = ''
    for (var p in data) {
        str += encodeURIComponent(p) + '=' + encodeURIComponent(data[p]) + '&'
    }
    return str
}

我記得之前封裝的時候一直不是很理解這個callback名字的道理,不是直接讀取options的callback就好了嗎,後來才知道這個是爲了在默認狀況下callbackName爲了避免重名,對其進行了改寫,後端代碼直接讀取callbackName就行。後端

後端讀取jsonp

app.get("https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su", function(req, res) {
    console.log("server accept: ", req.query.name, req.query.id)
    var data = "{" + "name:'" + req.query.name + " - server 3001 process'," + "id:'" + req.query.id + " - server 3001 process'" +"}"
    var callback = req.query.callback
    var jsonp = callback + '(' + data + ')'
    console.log(jsonp)
    res.send(jsonp)
    res.end()
})

這裏對data數據進行了序列化,data 中字符串拼接,不能直接將 JSON 格式的 data 直接傳給回調函數,不然會發生編譯錯誤: parsererror Error: jsonpCallback was not called。
注意:jsonp返回的不是json數據,而是一段能夠當即執行的js代碼,因此不會像 ajax 的 XmlHttpRequest 那樣能夠監聽不一樣事件對數據進行不一樣處理。因爲jq的jsonp進行了封裝,因此纔看起來想ajax同樣,有錯誤回調,其實jsonp的短板就是不能發post請求和不能註冊success,error等監聽函數跨域

cors跨域

如今用得比較多的跨域是cors,之前的flash和iframe跨域都存在的問題,這是一種新規範。所謂同源策略的限制實際上是跨域請求並不是是瀏覽器限制了發起跨站請求,而是請求能夠正常發起,到達服務器端,可是服務器返回的結果會被瀏覽器攔截。瀏覽器

原生封裝cors

function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {

    // "withCredentials"屬性是XMLHTTPRequest2中獨有的
    xhr.open(method, url, true);

  } else if (typeof XDomainRequest != "undefined") {

    // 檢測是否XDomainRequest可用
    xhr = new XDomainRequest();
    xhr.open(method, url);

  } else {

    // 看起來CORS根本不被支持
    xhr = null;

  }
  return xhr;
}

var xhr = createCORSRequest('GET', url);
if (!xhr) {
  throw new Error('CORS not supported');
}
// 絕大多數狀況下,咱們只須要和onload及onerror打交道,就像下面這樣:

xhr.onload = function() {
 var responseText = xhr.responseText;
 console.log(responseText);
 // 繼續其它代碼
};

xhr.onerror = function() {
  console.log('There was an error!');
};

對比下元素ajax

function AJAX(json) {
    var url = json.url,
        method = json.method,
        flag = json.flag,
        data = json.data,
        callBack = json.callBack,
        xhr = null;
    if(window.XMLHttpRequest) {
        xhr = new window.XMLHttpRequest();
    }else {
        xhr = new ActiveXObject('Mircosoft.XMLHTTP');
    }            
    if(method == 'get') {
        url += '?' + data + new Date().getTime(); 
        xhr.open('get', url, flag);
    }else {
        xhr.open('post', url, flag);
    }
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            // 數據已經可用了
            callBack(xhr.responseText);
        }
    }
    if(method == 'get') {
        xhr.send();
    }else {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urle');
        xhr.send(data);
    }    
}

服務器應答cors

app.post('/cors', function(req, res) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By", ' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    var data = {
        name: req.body.name + ' - server 3001 cors process',
        id: req.body.id + ' - server 3001 cors process'
    }
    console.log(data)
    res.send(data)
    res.end()
})

哈,感謝你的觀看,喜歡的話能夠點贊,收藏~~~
若是有什麼寫錯的地方,歡迎你們指出,一塊兒學習進步~~~~服務器

相關文章
相關標籤/搜索