(十四)登记读取路由与参数约束
1. 让路由只连接输入和业务动作
service.py 决定怎样查记录,路由负责从 HTTP 取得输入、调用业务函数,并声明输出。把书籍入口集中放进一个 APIRouter:
# 项目文件:api_project/src/bookapi/routes/books.py
from typing import Annotated
from fastapi import APIRouter, Path, Query, Response
from .. import service
from ..dependencies import CurrentIdentity, DbSession
from ..schemas import BookCreate, BookOut, BookPage, BookPatch
router = APIRouter(prefix="/books", tags=["书籍"])
# SQLite INTEGER 使用有符号 64 位编号;过大的编号在参数校验时交回 422。
BookId = Annotated[int, Path(ge=1, le=2**63 - 1)]
@router.get("", response_model=BookPage)
def read_books(
session: DbSession,
identity: CurrentIdentity,
q: Annotated[str, Query(max_length=100)] = "",
finished: bool | None = None,
offset: Annotated[int, Query(ge=0)] = 0,
limit: Annotated[int, Query(ge=1, le=100)] = 20,
):
return service.list_books(session, identity.user.id, q, finished, offset, limit)
@router.get("/{book_id}", response_model=BookOut)
def read_book(book_id: BookId, session: DbSession, identity: CurrentIdentity):
return service.owned_book(session, book_id, identity.user.id)APIRouter 的登记方式与 app 相似。prefix="/books" 为这组路由统一加上前缀,tags=["书籍"] 将它们归在接口页面的同一组。因此 @router.get("") 对应 GET /books,@router.get("/{book_id}") 对应单本读取。
BookId 把整数和 Path 检查组合成可复用的注解。SQLite 的整数编号保存为有符号 64 位值,这里从 1 到 2**63-1;请求超大编号时,在进入查询前得到字段错误。查询编号存在与否则由 owned_book 判断,编号 99 格式正确但没有记录,得到 404。
read_books 的三个部分可以顺着读:依赖取得会话和身份;查询参数取得筛选和分页;函数体将结果交给 service.list_books。finished 的 None 继续表示不筛选,offset=0 表示从第一条开始,limit=20 是没有提交条数时的默认值。