CORS 跨域

A: 預檢不過 …
B: … postman 沒問題啊
A: Ummm…瀏覽器不太一樣…

內文

  • 關於跨域
    • 同源政策
    • 請求類型
    • preflight
  • 如何解決跨域
    • 設置正確標頭
    • 使用套件

關於跨域


瀏覽器基於安全性的考量
有所謂的同源政策
即以 Ajax 發送請求時 默認情況只有同域名下才能互相存取
若 port 不同, 子域名不同或協定不同 皆屬於跨域

一般來說跨域只發生在瀏覽器
用伺服器直接發 Request 是沒問題的 (除非 API 本身就有問題 XD)

所以跨域實際上要解決的問題是瀏覽器機制的問題
為了解決此問題 我們會需要先知道瀏覽器發送 Request 的機制

當在瀏覽器發送 Request 時
主要分兩種類型的請求:

  • 簡單請求
  • 複雜請求

簡單請求包含以下兩點要素

  • 方法為
    • GET
    • POST
    • HEAD

  • 只含有簡單請求的標頭
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-type:
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain

除了簡單請求
就是複雜請求

而簡單請求與非簡單請求最大的差別就在於…!?

Preflight

如果是複雜請求的話
需要特別留意瀏覽器在正式請求前
會先發送一個 HTTP OPTIONS 的選項請求
當對應的標頭都正確無誤才會發送正式的 Request
這個行為被稱為 預檢

如何解決跨域


根據簡單請求與非簡單請求有不同處理方式
底下以 node express 來做示範

  • 簡單請求跨域:
    • API 設定 Access-Control-Allow-Origin 的跨域來源
1
2
3
4
5
6
7
8
9
10
11
12
const express = require("express");
const app = express();
const port = 3000;

app.get("/", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); // "*" 表示皆開放
res.send("Success!");
});

app.listen(port, function () {
console.log(`App listening on port ${port}!`);
});
  • 非簡單請求跨域:
    • API 設定 Access-Control-Allow-Origin 的跨域來源
    • 外加設定 OPTIONS 的端口
      • 設定 “Access-Control-Allow-Origin”
      • 設定 “Access-Control-Allow-Headers” (加上額外的 header)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const express = require("express");
const app = express();
const port = 3000;

app.options("/test", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Content-Type");
next();
});

app.post("/test", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.send(JSON.stringify({ token: "Z29vZG1hbg==" }));
});

app.listen(port, function () {
console.log(`App listening on port ${port}!`);
});
  • 使用套件 cors (簡單請求與複雜請求都可以)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const express = require("express");
const cors = require("cors");
const app = express();
const port = 3000;

app.use(cors());

app.post("/test", function (req, res, next) {
res.send(JSON.stringify({ token: "Z29vZG1hbg==" }));
});

app.listen(port, function () {
console.log(`App listening on port ${port}!`);
});

本文作者: David Huang
本文地址https://davidblog.github.io/2020/06/29/cors/

0%