Home Fastapi (5) CORS (中文)
Post
Cancel

Fastapi (5) CORS (中文)

CORS(Cross-Origin Resource Sharing)

CORS 是一種瀏覽器安全特性,用於限制來自不同域的網頁腳本如何與伺服器資源進行互動。 如果你在構建一個前後端分離的應用,前端和後端位於不同的域(或端口),你就需要配置 CORS。

為什麼? 簡單來講,如果不限制請求的來源,那只要知道 url 的不明來源都可以發請求,而且伺服器還很聽話的把資料傳給對方,如果涉及敏感資料,容易有安全性的問題。

在 FastAPI 中配置 CORS(跨域資源共享)非常簡單。

Fastapi codes(後端)

下面這段 code 示範沒有設置 CORS 的情況下,前端會 發生什麼錯誤。

功能描述:主要是有個 Item 的 class,可以讓前端發請求做 CRUD 的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    id: int
    name: str
    description: str = None

items = {}

@app.post("/items/")
def create_item(item: Item):
    if item.id in items:
        raise HTTPException(status_code=400, detail="Item already exists")
    items[item.id] = item
    return item

@app.get("/items/{item_id}")
def read_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return items[item_id]

@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    items[item_id] = item
    return item

@app.delete("/items/{item_id}")
def delete_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    del items[item_id]
    return {"detail": "Item deleted"}

前端 codes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!DOCTYPE html>
<html>
<head>
    <title>FastAPI CRUD Operations</title>
    <script>

        const baseURL = 'http://localhost:8000';

        async function createItem() {
            const id = document.getElementById('create-id').value;
            const name = document.getElementById('create-name').value;
            const description = document.getElementById('create-description').value;

            const response = await fetch(`${baseURL}/items/`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ id: parseInt(id), name: name, description: description })
            });
            const result = await response.json();
            alert(JSON.stringify(result));
        }

        async function readItem() {
            const id = document.getElementById('read-id').value;
            const response = await fetch(`${baseURL}/items/${id}`, { method: 'GET' });
            const result = await response.json();
            alert(JSON.stringify(result));
        }

        async function updateItem() {
            const id = document.getElementById('update-id').value;
            const name = document.getElementById('update-name').value;
            const description = document.getElementById('update-description').value;

            const response = await fetch(`${baseURL}/items/${id}`, {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ id: parseInt(id), name: name, description: description })
            });
            const result = await response.json();
            alert(JSON.stringify(result));
        }

        async function deleteItem() {
            const id = document.getElementById('delete-id').value;
            const response = await fetch(`${baseURL}/items/${id}`, { method: 'DELETE' });
            const result = await response.json();
            alert(JSON.stringify(result));
        }
    </script>
</head>
<body>
    <h1>FastAPI CRUD Operations</h1>

    <h2>Create Item</h2>
    <label>ID:</label><input type="text" id="create-id"><br>
    <label>Name:</label><input type="text" id="create-name"><br>
    <label>Description:</label><input type="text" id="create-description"><br>
    <button onclick="createItem()">Create</button><br>

    <h2>Read Item</h2>
    <label>ID:</label><input type="text" id="read-id"><br>
    <button onclick="readItem()">Read</button><br>

    <h2>Update Item</h2>
    <label>ID:</label><input type="text" id="update-id"><br>
    <label>Name:</label><input type="text" id="update-name"><br>
    <label>Description:</label><input type="text" id="update-description"><br>
    <button onclick="updateItem()">Update</button><br>

    <h2>Delete Item</h2>
    <label>ID:</label><input type="text" id="delete-id"><br>
    <button onclick="deleteItem()">Delete</button><br>
</body>
</html>

透過 OpenAPI 自動生成互動文件,先加入資料,再讓前端去讀

Desktop View

加入一筆 Item 資料

Desktop View

可以看到成功加入

Desktop View

現在透過前端去 Get,可以看到 server ok

Desktop View

但前端有 CORS 錯誤

Desktop View

在加入以下片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# 配置 CORS 中間件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost"],  # 允許的來源
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

#1. allow_origins:一個包含允許來源(origin)的列表。可以使用 ["*"] 來允許所有來源,但在生產環境中應盡量避免。

#2. allow_credentials:是否允許發送包含身份驗證信息的請求(如 cookies 或 HTTP 認證信息)。

#3. allow_methods:一個包含允許的 HTTP 方法的列表,如 ["GET", "POST"]。可以使用 ["*"] 來允許所有方法。

#4. allow_headers:一個包含允許的 HTTP 頭的列表,如 ["Content-Type", "Authorization"]。可以使用 ["*"] 來允許所有頭。

查詢剛剛的資料

Desktop View

更新該筆資料

Desktop View

再查一次,確定更新成功

Desktop View

刪除資料

Desktop View

再查一次,該筆資料消失

Desktop View

以上,這樣完整的 CRUD 都做到了!

☝ツ☝

This post is licensed under CC BY 4.0 by the author.

👈 ツ 👍