0
0
Lập trình
Harry Tran
Harry Tran106580903228332612117

Ngăn Chặn Property Drilling trong FastAPI với Request-Level Globals

Đăng vào 7 tháng trước

• 3 phút đọc

Ngăn Chặn Property Drilling trong FastAPI với Request-Level Globals

Giới Thiệu

Trong quá trình phát triển ứng dụng với FastAPI, việc truyền dữ liệu qua các lớp khác nhau có thể trở thành một cơn ác mộng, đặc biệt là khi bạn phải truyền cùng một thông tin như ngữ cảnh người dùng hoặc phiên database qua nhiều hàm và lớp. Bài viết này sẽ hướng dẫn bạn cách tối ưu hóa mã của mình bằng cách sử dụng biến ngữ cảnh ở cấp độ yêu cầu, giúp giảm thiểu việc truyền thông tin không cần thiết qua các lớp, đồng thời cải thiện khả năng kiểm tra và bảo trì mã.

Tình Huống Hiện Tại: Cơn Ác Mộng Property Drilling 🙄

Trước khi áp dụng giải pháp mới, mã của tôi trông như sau:

python Copy
# controller.py
@router.post("/projects")
async def create_project(
    data: ProjectRequest,
    db: Session = Depends(get_db),
    context: Context = Depends(get_context)
):
    return await project_service.create_project(data, context)

Mã này đã phải truyền context qua nhiều lớp chỉ để có thể kiểm tra quyền hạn trong chính sách. Điều này không chỉ làm mã trở nên khó đọc mà còn dễ dẫn đến lỗi.

Cảm Hứng Từ Nguồn Khác

Tôi đã tình cờ xem một video của DHH trên YouTube về việc viết phần mềm tốt. Ông đã đề cập đến khái niệm Current trong Ruby, và tôi đã quyết định áp dụng một cách tương tự trong FastAPI của mình. Dù không biết rõ cách hoạt động của nó trong Ruby, nhưng tôi đã cố gắng tái hiện ý tưởng này để làm giảm mã và cải thiện khả năng kiểm tra.

Giải Pháp: Sử Dụng Biến Ngữ Cảnh Ở Cấp Độ Yêu Cầu

Tạo Biến Ngữ Cảnh

Đầu tiên, chúng ta sẽ tạo một biến ngữ cảnh trong Python ngay từ đầu yêu cầu:

python Copy
# context.py
from contextvars import ContextVar
from pydantic import BaseModel

class Context(BaseModel):
    issuer_id: str
    issuer_role: str
    # Các thuộc tính khác...

Middleware Để Đặt Ngữ Cảnh

Chúng ta cần một middleware để thiết lập biến ngữ cảnh khi có yêu cầu đến:

python Copy
# middleware.py
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware

class CurrentContextMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        user = await get_current_user_from_request(request)
        context = Context.for_user(user)

        Current.set(context)

        try:
            response = await call_next(request)
            return response
        finally:
            Current.clear()

Mã Sau Khi Tối Ưu Hóa

Sau khi áp dụng biến ngữ cảnh, mã sẽ trông như sau:

python Copy
# controller.py
@router.post("/projects")
async def create_project(data: ProjectRequest):
    return await project_service.create_project(data)

Hướng Dẫn Thực Hành

Các Bước Thực Hiện

  1. Tạo lớp Context: Định nghĩa lớp Context để chứa thông tin cần thiết cho mỗi yêu cầu.
  2. Tạo middleware: Tạo middleware để thiết lập và xóa ngữ cảnh sau khi hoàn tất yêu cầu.
  3. Sử dụng ngữ cảnh: Trong các hàm dịch vụ và chính sách, thay vì truyền ngữ cảnh như trước, bạn chỉ cần truy cập vào nó khi cần thiết.

Tips Hiệu Suất

  • Giảm thiểu Mã: Việc ngừng truyền ngữ cảnh sẽ giúp mã trở nên ngắn gọn hơn, dễ đọc hơn.
  • Kiểm Tra Dễ Dàng: Nhờ vào việc giảm số lượng tham số truyền qua, việc kiểm tra cũng trở nên dễ dàng hơn.

Cảnh Báo Thường Gặp

  • Đảm bảo rằng biến ngữ cảnh được thiết lập đúng cách trong middleware để tránh lỗi khi không có ngữ cảnh.
  • Kiểm tra kỹ các quyền truy cập trước khi thực hiện bất kỳ hành động nào dựa trên ngữ cảnh.

Kết Luận

Sau khi áp dụng mô hình này:

  • Giảm 40% mã trong các lớp dịch vụ và kho dữ liệu.
  • Thiết lập kiểm tra giảm một nửa, không cần chuỗi mô phỏng phức tạp.

Cảm ơn DHH đã truyền cảm hứng cho giải pháp này! Nếu bạn muốn tìm hiểu thêm, hãy theo dõi blog của tôi để nhận thêm những thông tin và hướng dẫn thú vị khác!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào