昨天我們在後端導入了 JWT,讓 Admin 後台登入流程更安全。
但當前端開始呼叫後端 API(如 /orders
)時,很多人會遇到 CORS 錯誤。
今天先解釋什麼是 CORS,為什麼會出錯,並提供三種常見解法。
最後,我們會示範如何在後台串接 API,顯示訂單列表。
同源政策 (Same-Origin Policy):瀏覽器限制不同來源(協定 + 網域 + 埠號)的請求。
當前端 (http://localhost:5173) 要打後端 (http://localhost:3000) → 這就是跨來源。
瀏覽器會先送 預檢請求 (OPTIONS),伺服器必須回應允許的標頭,否則正式請求會被擋。
Console 紅字:
Access to fetch ... has been blocked by CORS policy
意思:伺服器沒有回 Access-Control-Allow-Origin
標頭。
const cors = require("cors");
app.use(cors({
origin: ["http://localhost:5173"],
methods: ["GET","POST","PUT","DELETE"],
allowedHeaders: ["Content-Type","Authorization"]
}));
// vite.config.js
export default defineConfig({
server: {
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, "")
}
}
}
})
axios.get("/api/orders")
用 Express express.static
提供前端頁面 + API。
前端直接呼叫相對路徑 /orders
,不存在跨域問題。
建議先用 方式 A (cors middleware),最直觀。
進階讀者可用 B 或 C。
const express = require("express");
const router = express.Router();
const { authAdmin } = require("./auth");
const Order = require("../models/order.model");
router.get("/", authAdmin, async (req, res) => {
const orders = await Order.find().sort({ createdAt: -1 });
res.json({ success: true, orders });
});
module.exports = router;
import { useEffect, useState } from "react";
import axios from "axios";
export default function Dashboard() {
const [orders, setOrders] = useState([]);
useEffect(() => {
const token = localStorage.getItem("jwt");
axios.get("http://localhost:3000/orders", {
headers: { Authorization: `Bearer ${token}` }
})
.then(res => setOrders(res.data.orders))
.catch(err => console.error(err));
}, []);
return (
<div>
<h1>訂單管理</h1>
<ul>
{orders.map(o => (
<li key={o._id}>
{o.items.map(i => i.productName).join(", ")} - {o.status}
</li>
))}
</ul>
</div>
);
}
成功登入之後就會被導向到 dashboard 的 router,再來就能看到目前訂單的狀況啦!
當瀏覽器發送跨域、且不屬於「簡單請求」的情況(例如 Content-Type: application/json
或帶 Authorization
標頭)時,會先送一個 OPTIONS
請求到伺服器,詢問:
允許哪些來源 (Access-Control-Allow-Origin)
允許哪些方法 (Access-Control-Allow-Methods)
允許哪些自訂標頭 (Access-Control-Allow-Headers)
如果伺服器沒有正確回應這些標頭,瀏覽器就會擋下真正的 POST/PUT/DELETE 請求。這就是為什麼我們需要在 Express 裡加上:
app.options("*", cors());
來處理所有的預檢請求。
CORS 是開發中最常遇到的坑,記得從伺服器或代理處理。
後台 Dashboard 現在能顯示訂單列表,之後可以加上「修改訂單狀態」功能。
明天(Day 20)我們將實作 訂單狀態修改,讓後台可以直接改 Pending → In Progress → Completed。