Khi API cần đọc header tùy chỉnh như X-Request-ID hay cookie phiên làm việc, đừng moi thủ công từ request.headers và request.cookies rồi tự kiểm tra từng trường hợp thiếu giá trị. FastAPI có sẵn Header() và Cookie(): khai báo chúng như tham số hàm, framework tự trích xuất, validate và trả lỗi 422 rõ ràng khi thiếu hoặc sai kiểu. Dùng hai công cụ này khi cần đọc metadata của request mà không tự parse thủ công.

Vấn đề

Giả sử endpoint cần ghi log theo mã trace X-Request-ID do client gửi kèm, đồng thời đọc cookie session_id để biết người dùng đã đăng nhập hay chưa. Cách thủ công thường trông như sau:

from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int, request: Request):
request_id = request.headers.get("x-request-id")
session_id = request.cookies.get("session_id")
if not session_id:
raise HTTPException(status_code=401, detail="Chưa đăng nhập")
return {"item_id": item_id, "request_id": request_id}

Cách này chạy được, nhưng có ba điểm yếu. Thứ nhất, tên header không phân biệt hoa thường, còn code thủ công dễ gõ sai hoặc quên chuẩn hóa. Thứ hai, không có validate: giá trị sai định dạng vẫn chui qua, lỗi chỉ lộ ra ở tầng xử lý sâu bên trong. Thứ ba, mỗi endpoint lặp lại đoạn moi và kiểm tra, signature không nói rõ endpoint cần dữ liệu gì.

Giải pháp

Khai báo header và cookie ngay trong signature bằng Header() và Cookie():

from typing import Annotated
from fastapi import Cookie, FastAPI, Header, HTTPException
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(
item_id: int,
x_request_id: Annotated[str | None, Header()] = None,
session_id: Annotated[str | None, Cookie()] = None,
):
if session_id is None:
raise HTTPException(status_code=401, detail="Chưa đăng nhập")
return {"item_id": item_id, "request_id": x_request_id}

Quy tắc đặt tên rất gọn: FastAPI tự đổi dấu gạch dưới thành dấu gạch ngang, nên tham số x_request_id tương ứng với header X-Request-ID, còn tên cookie giữ nguyên. Đặt mặc định None nghĩa là tùy chọn, bỏ mặc định nghĩa là bắt buộc — thiếu là client nhận ngay lỗi 422 kèm tên trường thiếu, thay vì lỗi khó hiểu ở tầng sau. Muốn ràng buộc thêm, truyền thẳng điều kiện vào khai báo, ví dụ Header(min_length=8). Vì chúng là tham số khai báo, tài liệu OpenAPI tự hiển thị, và test chỉ cần truyền tham số, không cần dựng Request giả.

Áp dụng vào dự án của bạn

Bắt đầu từ một nơi duy nhất: nhận header X-Request-ID ở mọi endpoint quan trọng và ghi nó vào log, khi sự cố xảy ra bạn lần theo một mã duy nhất thay vì mò từng dòng log rời rạc. Tiếp theo, dùng cookie cho dữ liệu không nhạy cảm như ngôn ngữ giao diện hay theme, còn token xác thực thì giữ trong cookie HttpOnly và đọc bằng Cookie() ở phía server. Nâng cao hơn: nhận header Idempotency-Key cho endpoint thanh toán, lưu kết quả theo key đó để client gửi lại cùng request không bị trừ tiền hai lần.

Bài viết liên quan

Câu hỏi thường gặp

Header() và Cookie() khác gì so với đọc từ Request?

Đọc từ Request không có validate, không hiện trong tài liệu OpenAPI và phải tự xử lý giá trị thiếu. Header() và Cookie() khai báo rõ dữ liệu trong signature, framework lo trích xuất và báo lỗi chuẩn.

Header bắt buộc hay tùy chọn, quyết định thế nào?

Không đặt giá trị mặc định nghĩa là bắt buộc, thiếu sẽ trả 422. Đặt mặc định None nghĩa là tùy chọn. Quy tắc chung: dữ liệu không thể thiếu thì để bắt buộc, metadata như mã trace thì để tùy chọn.

Tên header có dấu gạch ngang thì khai báo ra sao?

Dùng tên tham số với dấu gạch dưới, ví dụ x_request_id, FastAPI tự đổi thành x-request-id. Nếu tên header chứa ký tự không đặt được thành tên biến Python, truyền tên thật qua tham số alias, ví dụ Header(alias=”X-Custom-Name”).

Cookie HttpOnly có đọc được bằng Cookie() không?

Có, vì HttpOnly chỉ chặn JavaScript phía trình duyệt đọc cookie, không chặn server. Server vẫn nhận cookie trong mỗi request và Cookie() đọc bình thường, nên đây vẫn là cách an toàn để giữ token xác thực.

💬 Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.

Nguồn tham khảo