1. 程式人生 > 其它 >FastAPI(37)- Middleware 中介軟體

FastAPI(37)- Middleware 中介軟體

什麼是中介軟體

  • 就是一個函式,它在被任何特定路徑操作處理之前處理每個請求,且在每個 response 返回之前被呼叫
  • 類似鉤子函式

執行順序

  1. 中介軟體會接收應用程式中的每個請求 Request
  2. 針對請求 Request 或其他功能,可以自定義程式碼塊
  3. 再將請求 Request 傳回路徑操作函式,由應用程式的其餘部分繼續處理該請求
  4. 路徑操作函式處理完後,中介軟體會獲取到應用程式生成的響應 Response
  5. 中介軟體可以針對響應 Response 或其他功能,又可以自定義程式碼塊
  6. 最後返回響應 Response 給客戶端

Request

FastAPI 有提供 Request 模組,但其實就是 starlette 裡面的 Request

Response

FastAPI 有提供 Response 模組,但其實就是 starlette 裡面的 Response

中介軟體和包含 yield 的依賴項、Background task 的執行順序

  1. 依賴項 yield 語句前的程式碼塊
  2. 中介軟體
  3. 依賴項 yield 語句後的程式碼塊
  4. Background task

建立中介軟體

import time
from fastapi import FastAPI, Request


@app.middleware("http")
# 必須用 async
async def add_process_time_header(request: Request, call_next):
    start_time 
= time.time() # 必須用 await response = await call_next(request) process_time = time.time() - start_time # 自定義請求頭 response.headers["X-Process-Time"] = str(process_time) # 返回響應 return response

中介軟體函式接收兩個引數

  • request:Request 請求,其實就是 starlette 庫裡面的 Request
  • call_next:是一個函式,將 request 作為引數

call_next

  • 會將 request 傳遞給相應的路徑操作函式
  • 然後會返回路徑操作函式產生的響應,賦值給 response
  • 可以在中介軟體 return 前對 response 進行操作

實際栗子

import uvicorn
from fastapi import FastAPI, Request, Query, Body, status
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()


@app.middleware("http")
# 必須用 async
async def add_process_time_header(request: Request, call_next):
    # 1、可針對 Request 或其他功能,自定義程式碼塊
    print("=== 針對 request 或其他功能執行自定義邏輯程式碼塊 ===")
    print(request.query_params)
    print(request.method)

    # 2、將 Request 傳回給對應的路徑操作函式繼續處理請求
    # 必須用 await
    response = await call_next(request)
    # 4、接收到路徑操作函式所產生的的 Response,記住這並不是返回值(return)

    # 5、可針對 Response 或其他功能,自定義程式碼塊
    print("*** 針對 response 或其他功能執行自定義邏輯 ***")

    # 自定義請求頭響應狀態碼
    response.headers["X-Process-Token"] = str("test_token_polo")
    response.status_code = status.HTTP_202_ACCEPTED

    # 6、最終返回 Response 給客戶端
    return response


class User(BaseModel):
    name: str = None
    age: int = None


@app.post("/items/")
async def read_item(item_id: str = Query(...), user: User = Body(...)):
    # 3、收到請求,處理請求
    res = {"item_id": item_id}
    if user:
        res.update(jsonable_encoder(user))
    print("@@@ 執行路徑操作函式 @@@", res)
    
    # 有沒有 return 都不影響中介軟體接收 Response
    return res

重點

  • call_next 是一個函式,呼叫的就是請求路徑對應的路徑操作函式
  • 返回值是一個 Response 型別的物件

訪問 /items ,控制檯輸出結果

=== 針對 request 或其他功能執行自定義邏輯程式碼塊 ===
item_id=test
POST
@@@ 執行路徑操作函式 @@@ {'item_id': 'test', 'name': 'string', 'age': 0}
*** 針對 response 或其他功能執行自定義邏輯 ***

從請求結果再看執行流程圖

  • 黃色塊就是業務程式碼啦
  • 紅色線就是處理完 Request,準備返回 Response 了

正常傳參的請求結果

自定義的請求頭和響應碼已經生效啦