已发布·持续改进
FastAPI 指南 · 3/6
本章目前仅提供英文版。
Almost everything in FastAPI follows from one idea: declare it with type hints and the framework handles the rest. This chapter walks through path operations, path, query and body parameters, Pydantic models and validation, dependency injection, async def versus def, and the OpenAPI docs.
FastAPI calls the combination of an HTTP method and a path a path operation. You declare one with @app.get, @app.post, @app.put, @app.patch or @app.delete, and that is also where you set the status code and response model.
from fastapi import FastAPI, HTTPException, status
app = FastAPI()
items: dict[int, str] = {1: "Keyboard"}
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
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]Raising HTTPException stops processing immediately and returns the given status code with a {"detail": ...} JSON body. Routes are matched in the order they are declared, so a fixed path such as /users/me must come before /users/{user_id}.
A function parameter whose name matches a {name} in the path is a path parameter; any other parameter with a simple type is a query parameter. Extra validation rules are attached with Annotated plus Path or Query.
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/users/{user_id}/items")
def list_user_items(
user_id: Annotated[int, Path(ge=1)],
q: Annotated[str | None, Query(max_length=50)] = None,
skip: int = 0,
limit: Annotated[int, Query(le=100)] = 10,
):
return {"user_id": user_id, "q": q, "skip": skip, "limit": limit}A parameter without a default is required; one with a default is optional. Constraints such as ge, le, min_length and pattern drive validation and show up in the OpenAPI docs as well.
JSON bodies are declared as classes that inherit from Pydantic's BaseModel. When a parameter's type is a Pydantic model, FastAPI reads it from the request body.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class ItemCreate(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0)
tags: list[str] = []
class ItemPublic(BaseModel):
id: int
name: str
price: float
@app.post("/items", response_model=ItemPublic, status_code=201)
def create_item(item: ItemCreate):
return ItemPublic(id=1, **item.model_dump())If the body is not valid JSON or price is negative, the client automatically gets a 422 response. response_model filters the output down to that model's shape, which keeps fields that only belong on input, such as passwords, from leaking into responses. A return annotation (-> ItemPublic) achieves the same thing.
Depends is FastAPI's dependency injection mechanism. Declare a function (or class) as a dependency and FastAPI calls it for each request and passes the result to your path operation. Dependencies can take path and query parameters of their own and can depend on other dependencies.
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def pagination(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
Pagination = Annotated[dict, Depends(pagination)]
@app.get("/items")
def list_items(page: Pagination):
return {"page": page}
@app.get("/users")
def list_users(page: Pagination):
return {"page": page}An Annotated type alias lets several routes reuse the same dependency with very little code. Dependencies that use yield can run cleanup code after the response, which is why they are the usual way to manage database sessions (chapter 4). Authentication, permission checks and shared parameters are typically implemented as dependencies too.
Path operations can be declared with either async def or plain def, and FastAPI runs them differently:
| Declaration | How it runs | Use it when |
|---|---|---|
async def | directly on the event loop | you call awaitable async libraries (HTTPX, asyncpg and so on) |
def | in a separate thread pool | you call blocking libraries (classic DB drivers, file processing) |
The trap to avoid is calling blocking code, such as time.sleep() or a synchronous database call, inside an async def function: it stalls the entire event loop. When in doubt, plain def is the safer choice.
import httpx
from fastapi import FastAPI
app = FastAPI()
@app.get("/weather")
async def weather():
async with httpx.AsyncClient() as client:
response = await client.get("https://example.com/api/weather")
return response.json()FastAPI collects every declaration above into an OpenAPI schema. Arguments such as summary, description, tags and deprecated make the docs friendlier, and a function's docstring becomes its description. Set the app's title and version with FastAPI(title=..., version=...).
HTTPException.Annotated, Path and Query.response_model.Depends, and keep blocking code in plain def functions.
0 条评论
登录 · 登录后即可发表评论。
来发表第一条评论吧。