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, Requestapp = 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 Annotatedfrom fastapi import Cookie, FastAPI, Header, HTTPExceptionapp = 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
- FastAPI: bảo vệ route bằng dependency JWT/OAuth2 tái sử dụng, không lặp code xác thực
- FastAPI: đặt cookie đăng nhập an toàn bằng Response.set_cookie trong 5 phút
- FastAPI: gom cả chục query param vào một Pydantic model — validate tập trung trong 5 phút
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
- Tài liệu FastAPI, mục Header Parameters: https://fastapi.tiangolo.com/tutorial/header-params/
- Tài liệu FastAPI, mục Cookie Parameters: https://fastapi.tiangolo.com/tutorial/cookie-params/
