深入跨域問題(2) - 利用 CORS 解決跨域

leiting1998發表於2018-05-17

閱讀目錄:

深入跨域問題(1) - 初識 CORS 跨域資源共享

深入跨域問題(2) - 利用 CORS 解決跨域(本篇)

深入跨域問題(3) - 利用 JSONP 解決跨域

深入跨域問題(4) - 利用 代理伺服器 解決跨域

回答問題:

在閱讀這篇文章之前,讀讀第一篇文章,效果會更好!!!回答上一篇文章的問題:

  1. 不會觸發 預請求 ,因為是簡單的 GET 方法。
  2. 不會觸發 預請求,因為是簡單的 POST 方法,jquery 預設使用 application/x-www-form-urlencoded
  3. 會觸發 預請求 ,雖然是 POST 方法,但是 Content-Typeapplication/json 。並非上文中的三種情況,text/plainmultipart/form-dataapplication/x-www-form-urlencoded

第三點,是最重要的一點,也是經常出錯的一點,記住觸發預請求三種情況 !!!

搭建跨域環境:

模擬客戶端請求:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Ajax測試</title>
</head>
<body>
    <script src="./node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        $.ajax({
            url: "http://localhost:3000",
            type: "get",
            success: function (result) {
                console.log(result);
            },
            error: function (msg) {
                console.log(msg);
            }
        })
    </script>
</body>
</html>
複製程式碼

後文,將不再貼上 html 程式碼,jquery 也可以用 npm install jquery 下載。

模擬伺服器響應,新建 app.js 檔案,貼上並執行

const http = require('http');

const server = http.createServer((request, response) => {
    if (request.url === '/') {
        if (request.method === 'GET') {
            response.end("{name: 'BruceLee', password: '123456'}");
        }
        
        if (request.method === 'POST') {
            response.end("true");
        }
    }

    response.end('false');
});

server.listen(3000, () => {
    console.log('The server is running at http://localhost:3000');
});
複製程式碼

好了,現在雙擊開啟 html 檔案,就成了:

深入跨域問題(2) - 利用 CORS 解決跨域

很明顯,這就是跨域報錯。

處理非預請求

在上述兩個例子中,我們說到,POST 和 GET 方法,都可以實現,非預請求。

關鍵程式碼:設定一條 響應首部欄位,允許 CORS 跨域資源共享:

response.writeHead(200, {
  'Access-Control-Allow-Origin': '*
}
複製程式碼

這裡,我們設定為,所有客戶端都可以訪問。

完整程式碼:

const http = require('http');

const server = http.createServer((request, response) => {
    if (request.url === '/') {
        if (request.method === 'GET') {
            
            response.writeHead(200, {
                'Access-Control-Allow-Origin': '*' // 關鍵程式碼
            });
            
            response.end("{name: 'BruceLee', password: '123456'}");
        }
        
        if (request.method === 'POST') {
            
            response.writeHead(200, {
                'Access-Control-Allow-Origin': '*' // 關鍵程式碼
            });
            
            response.end("true");
        }
    }

    response.end('false');
});

server.listen(3000, () => {
    console.log('The server is running at http://localhost:3000');
});
複製程式碼

前端測試程式碼:

$.ajax({
    url: "http://localhost:3000",
    type: "get",
    success: function (result) {
        console.log(result);
    },
    error: function (msg) {
        console.log(msg);
    }
})


var data = { name: 'BruceLee', password: '123456' };

$.ajax({
    url: "http://localhost:3000",
    type: "post",
    data: JSON.stringify(data),
    success: function (result) {
        console.log(result);
    },
    error: function (msg) {
        console.log(msg);
    }
})
複製程式碼

執行結果:

GET方法請求結果

POST方法請求結果

處理 非預請求 就是這麼簡單,只需要在後臺設定,一條 響應首部欄位 即可。

注意:我們使用 POST 方法時,Jquery 預設使用的 Content-Type: application/x-www-form-urlencoded,所以不會觸發預請求 !!!

事實上,不僅僅是 Jquery ,axios 等封裝 Ajax 的庫,都預設採用 Content-Type: application/x-www-form-urlencoded !!!

深入跨域問題(2) - 利用 CORS 解決跨域

處理 POST 預請求

不僅僅是 POST ,所有 預請求 的處理方式都一樣。

POST 方法在設定 contentTypeapplication/json 時會觸發預請求。

前端測試程式碼:

var data = { name: 'BruceLee', password: '123456' };

$.ajax({
    url: "http://localhost:3000",
    type: "post",
    data: JSON.stringify(data),
    contentType: 'application/json;charset=utf-8',
    success: function (result) {
        console.log(result);
    },
    error: function (msg) {
        console.log(msg);
    }
})
複製程式碼

注意,這裡的 contentType 已經修改為 application/json ,這種情況是會觸發 預請求 的 ! ! !

node 服務端程式碼:

const http = require('http');

const server = http.createServer((request, response) => {
    if (request.url === '/') {
        if (request.method === 'GET') {
            response.writeHead(200, {
                'Access-Control-Allow-Origin': '*'
            });
            response.end("{name: 'BruceLee', password: '123456'}");
        }

        if (request.method === 'POST') {
            response.writeHead(200, {
                'Access-Control-Allow-Origin': '*'
            });

            response.end( JSON.stringify({state: true}) );
        }

        if (request.method === 'OPTIONS') {	
    		
            response.end( JSON.stringify({state: true}) );
        }
    }

    response.end('false');
});

server.listen(3000, () => {
    console.log('The server is running at http://localhost:3000');
});
複製程式碼

在這裡,我們增加了處理 OPTIONS 方法的邏輯。

測試結果:

深入跨域問題(2) - 利用 CORS 解決跨域

很明顯,我們在 OPTIONS 方法內部沒有設定 CORS 響應首部欄位 ,所以出現跨域錯誤;

修改程式碼,關鍵程式碼:

if (request.method === 'OPTIONS') {	
    response.writeHead(200, {
        'Access-Control-Allow-Origin': '*',	 // 設定 optins 方法允許所有伺服器訪問 
        'Access-Control-Allow-Methods': '*', // 允許訪問 POST PUT DELETE 等所有方法 
    });		
    response.end( JSON.stringify({state: true}) );
}
複製程式碼

在 node 程式碼中,我們增加對 OPTIONS 的處理,並且設定允許訪問, POST 方法。

修改程式碼後,重啟伺服器,並重新整理 html 頁面,結果為:

深入跨域問題(2) - 利用 CORS 解決跨域

在這裡,仍然是有問題,按照報錯描述,我們應該設定 Access-Control-Allow-Headers 響應首部欄位 。

關鍵程式碼:


if (request.method === 'OPTIONS') {	
    response.writeHead(200, {
        'Access-Control-Allow-Origin': '*',	 // 設定 optins 方法允許所有伺服器訪問 
        'Access-Control-Allow-Methods': '*', // 允許訪問路徑 '/' POST等所有方法
        'Access-Control-Allow-Headers': 'Content-Type',	// 允許類 Content-Type 頭部 
    });	
    response.end( JSON.stringify({state: true}) );
}
複製程式碼

我們需要設定,允許使用頭部為 Content-Type 的內容訪問。

完整程式碼:

const http = require('http');

const server = http.createServer((request, response) => {
    if (request.url === '/') {
        if (request.method === 'GET') {
            response.writeHead(200, {
                'Access-Control-Allow-Origin': '*'
            });
            response.end("{name: 'BruceLee', password: '123456'}");
        }

        if (request.method === 'POST') {
            response.writeHead(200, {
                'Access-Control-Allow-Origin': '*'
            });

            response.end( JSON.stringify({state: true}) );
        }

        if (request.method === 'OPTIONS') {	
            response.writeHead(200, {
                'Access-Control-Allow-Origin': '*',	 // 設定 optins 方法允許所有伺服器訪問 
                'Access-Control-Allow-Methods': '*', // 允許訪問路徑 '/' POST等所有方法
                'Access-Control-Allow-Headers': 'Content-Type',	// 允許類 Content-Type 頭部 
            });
        }
    }

    response.end('false');
});

server.listen(3000, () => {
    console.log('The server is running at http://localhost:3000');
});
複製程式碼

執行結果:

  1. 預請求

    深入跨域問題(2) - 利用 CORS 解決跨域

  2. POST 請求

深入跨域問題(2) - 利用 CORS 解決跨域

這樣就完成,對 預請求 的處理。現在你可以狠狠地告訴後臺:是你沒有處理 OPTIONS 方法 !!!

深入跨域問題(2) - 利用 CORS 解決跨域

好了,到這裡,知道了基礎的 預請求 處理的解決辦法了。

總結:

  1. 使用 CORS 跨域資源共享,是需要分成 預請求非預請求 處理的。

  2. 非預請求,在伺服器內,只需要簡單設定:

    'Access-Control-Allow-Origin': '*
    複製程式碼
  3. 預請求,在伺服器內,至少要設定三個 響應首部欄位

    'Access-Control-Allow-Origin': ?,	  
    'Access-Control-Allow-Methods': ?, 
    'Access-Control-Allow-Headers': 'Content-Type',
    複製程式碼
  4. 前端使用 content-Type: application/json 的時候,必須注意這是 預請求 ,後端需要處理 OPTIONS 方法

參考與鳴謝:

祝大家編碼愉快,喜歡的話點個贊再走唄!!!

相關文章