前端跨域(一):CORS

上週作了一個移動端表單提交的頁面,其中涉及到了跨域問題,想來也是慚愧,由於以前一直都沒有遇到過這個問題,所以都沒有深刻探索過,只是知道有哪幾種方式,此次終於借這個機會能夠把遺留的知識點補一補了。javascript

1. CORS(Cross-Origin Resource Sharing,跨源資源共享)

【基本思想】:使用自定義的 HTTP 頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功仍是失敗。html

【實現方式】:
瀏覽器在發送請求時,檢測到跨域時,會自動加上一個額外的 Origin 頭部,其中包含請求頁面的原信息(協議、域名和端口),以便服務器根據這個頭部信息來決定是否給予響應。前端

Origin: http://www.nczonline.net

若是服務器認爲這個請求能夠接受,就在 Access-Control-Allow-Origin 頭部中回發相同的原信息(若是是公共資源,能夠回發「*」)。java

Access-Control-Allow-Origin: http://www.nczonline.net

若是沒有這個頭部,或者有着頭部但原信息不匹配,瀏覽器就會駁回請求。webpack

注意:默認狀況下,請求和響應都不包含 cookie 信息。git

2. 跨域場景復原

爲了模擬跨域,在本身的本地起了2個服務,一個採用 webpack 充當靜態資源服務器(webpack 腳手架可參考:scaffoldsForFE),另外一個用 Node 搭建,充當接受請求的服務器,分別給兩個服務器分配了不一樣的端口號。github

Client:http://localhost:8000web

var oDiv = document.getElementById('content');

var xhr = new XMLHttpRequest();
xhr.onload = function() {   // 響應接收完畢後將觸發 onload 事件
    if (xhr.readyState == 4) {
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            oDiv.innerHTML = 'Request was success:' + xhr.responseText;
            console.log('Request was success:', xhr.responseText);
        } else {
            oDiv.innerHTML = "Request was unsuccessful: " + xhr.status;
            console.log("Request was unsuccessful: ", xhr.status);
        }
    }
}

xhr.open('get', 'http://localhost:8000', true);  // 不跨域
// xhr.open('get', 'http://localhost:8888', true);  // 跨域
xhr.send();

Server:http://localhost:8888跨域

var http = require('http');

http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('This is a server test page.');
    res.end();
}).listen(8888);

當 Client 端向 http://localhost:8000 發請求時,不存在跨域,可以成功返回頁面信息,此時的頁面及其發請求的 Request Headers 以下:瀏覽器

不存在跨域

當 Client 端向 http://localhost:8888 請求服務的時候,因爲存在跨域問題,沒法得到響應:

Not Allowed Access

可是其請求的頭部自動帶上了 Origin 字段,並且因爲是默認狀況,沒有帶上 Cookie:

Request Headers

解決的方式是,在 Server 端響應的時候,在 Access-Control-Allow-Origin 頭部中回發相應的原信息:

var http = require('http');

http.createServer(function(req, res) {
    // 設置響應頭部
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('This is a server test page.');
    res.end();
}).listen(8888);

再從新請求,成功得到響應信息,且請求沒有帶上 Cookie:

Allowed Access

因爲在跨域時,默認狀況下是不容許客戶端向服務器發送請求時帶上 Cookie 的,那怎樣才能帶上 Cookie 呢?須要同時在客戶端和服務端同時設置相應字段:

(1)客戶端在請求中打開 withCredentials 屬性,指定某個請求應該發送憑據:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

(2)服務端在響應頭部中添加 Access-Control-Allow-Credentials 字段,且設爲 true,代表服務器接受帶憑據的請求:

res.setHeader('Access-Control-Allow-Credentials', true);

請求帶上 Cookie

若是發送的是帶憑據的請求,但服務器的響應中沒有包含這個頭部,那麼瀏覽器就不會把響應交給 Javascript,則 responseText 是空字符串,status 的值爲0,並且會調用 onerror() 事件處理程序。

cookie 是能夠設置訪問域的,在設置 cookie 的時候,設定了 cookie 的訪問域名爲一個頂級域名,則能夠達到幾個子域名共享 cookie 的效果,如騰訊網 www.qq.com 與微信網頁版 wx.qq.com 共享了 pac_uid,關於前端存儲的相關內容,可參考我以前在博客園寫的博文:前端存儲調研總結

3. 模擬 GET 帶參數 及 POST 跨域請求

i. GET 帶參數跨域請求

Client 端:經過在URL後面加上查詢參數

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.onload = function() {   // 響應接收完畢後將觸發 onload 事件
    // 處理 xhr.responseText
}

xhr.open('get', 'http://localhost:8888?method=GET&name=Ruth', true);
xhr.send();

Server 端:處理 GET 請求

var http = require('http');
var url = require('url');

http.createServer(function(req, res) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
    res.setHeader('Access-Control-Allow-Credentials', true);
    res.writeHead(200, {'Content-Type': 'text/plain'});

    // 解析 url 參數
    var params = url.parse(req.url, true).query;
    res.write('請求類型:' + params.method);
    res.write('<br />');
    res.write('姓名:' + params.name);
    res.end();
}).listen(8888);

GET with Query

ii. POST 跨域請求

不一樣於 JSONP,CORS 的好處就是可讓咱們實現 POST 請求。

Client 端發送信息:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.onload = function() {   // 響應接收完畢後將觸發 onload 事件
    // 處理 xhr.responseText
}

xhr.open('post', 'http://localhost:8888', true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");  // 設置請求頭
xhr.send('method=POST&name=Ruth');

Server 端處理請求:

var http = require('http');
var querystring = require('querystring');

http.createServer(function(req, res) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
    res.setHeader('Access-Control-Allow-Credentials', true);
    res.writeHead(200, {'Content-Type': 'text/plain'});

    var post = '';
    // 經過req的data事件監聽函數,每當接受到請求體的數據,就累加到post變量中
    req.on('data', function(chunk) {
        post += chunk;
    });

    // 在end事件觸發後,經過querystring.parse將post解析爲真正的POST請求格式,而後向客戶端返回
    req.on('end', function() {
        post = querystring.parse(post);
        res.write('請求類型:' + post.method);
        res.write('<br/>')
        res.write('姓名:' + post.name);
        res.end();
    });
}).listent(8888);

POST request

4. 參考資料

阮一峯-跨域資源共享 CORS 詳解
AJAX POST&跨域 解決方案 - CORS
前端跨域的整理

相關文章
相關標籤/搜索