FastAPI实战笔记(一) 基本介绍与简单操作
2026/5/10 19:38:43 网站建设 项目流程

一、基本介绍

基础定义

业界常用开发模型

  • /src目录:存放核心应用代码。其下可细分多个子模块:
    • models子目录:存放数据库模型定义
    • routes子目录:管理FastAPI路由配置
    • services子目录:封装业务逻辑层
  • /tests目录:将测试代码与主应用隔离,便于测试管理并确保生产构建不包含测试代码
  • /docs目录:集中存放API文档、安装指南及使用说明等关键文档资源

异步兼容

# 异步函数应仅用于I/O密集型操作(数据库查询、HTTP请求等)@app.get("/")asyncdefread_root():return{"Hello":"World"}

端点

端点是API交互的接入点。在FastAPI中,通过HTTP方法装饰器(如@app.get("/"))创建端点,表示应用根路径的GET请求处理器:

fromfastapiimportFastAPI app=FastAPI()# 当向根URL("/")发起GET请求时,read_root函数被调用并返回JSON响应@app.get("/")asyncdefread_root():return{"Hello":"World"}

路由

当需要管理跨文件的多个端点时,路由机制尤为重要。路由将端点分组到不同模块,大幅提升代码可维护性与可读性(例如:用户操作路由与产品操作路由分离)。

# 创建路由 router_example.pyfromfastapiimportAPIRouter router=APIRouter()@router.get("/items/{item_id}")asyncdefread_item(item_id:int):return{"item_id":item_id}
# 挂载路由 main.py# 路由模块应遵循单一职责原则,按业务域垂直拆分importrouter_examplefromfastapiimportFastAPI app=FastAPI()app.include_router(router_example.router)@app.get("/")asyncdefread_root():return{"Hello":"World"}
uvicorn main:app --reload#--reload参数使服务器在代码变更后自动重启,是开发环境的理想选择# 生产环境则应使用--workers参数配置多进程uvicorn main:app --reload --host192.168.31.158# 限制运行在特定的IP
http://127.0.0.1:8000 http://127.0.0.1:8000/docs http://127.0.0.1:8000/redoc

书店系统案例

后端雏形

# 书店系统后端雏形fromfastapiimportFastAPI app=FastAPI()# {book_id}是路径参数 用来动态传递值@app.get("/books/{book_id}")# book_id: int 执行了数据验证 防御常见注入攻击asyncdefread_book(book_id:int):return{"book_id":book_id,"title":"The Great Gatsby","author":"F. Scott Fitzgerald"}

[!NOTE]

RESTful API 资源标识规范

  1. REST 是面向资源的,路径应表示资源,而不是操作。
  • 不推荐:/getBook/{id}/deleteUser/{userId}
  • 推荐:/books/{book_id}/users/{user_id}

路径本身表示资源集合(/books)或具体资源(/books/123),操作由 HTTP 方法(GET/POST/PUT/DELETE)表达。

  1. 使用小写、下划线或短横线分隔(推荐短横线-

推荐:/books/{book-id}或直接/books/{id}

  1. 路径参数名应简洁,避免冗余

既然路径已经是/books/...,那么参数名无需重复 “book”:

  • 冗余:/books/{book_id}
  • 简洁:/books/{id}

这是 RESTful 设计中的通用惯例。例如 GitHub API 使用:/repos/{owner}/{repo},而不是/repo/{repo_owner}/{repo_name}

  1. 使用复数形式表示资源集合
  • /books(资源集合)
  • /books/123(具体资源实例)
  1. 标识符应为不透明的(opaque)且稳定
  • 使用数据库主键(如整数123)或全局唯一 ID(如 UUIDa1b2c3d4)。
  • 不应暴露内部结构(如/books/user123_book456)。
  • 一旦分配,不应改变(避免破坏链接)。

参数处理

# 路径参数适用于资源标识(如/users/{user_id})# 新增路径参数端点 用于检索作者信息@app.get("/authors/{author_id}")# 通过Python类型提示(author_id: int)自动执行参数验证与转换asyncdefread_author(author_id:int):return{"author_id":author_id,"name":"Ernest Hemingway"}
  1. 查询参数用于细化或定制API端点的响应,以问号(?)后追加的形式出现在URL中。例如,/books?genre=fiction&year=2010可能仅返回2010年出版的虚构类书籍。

为现有端点添加查询参数。假设我们需要允许用户按出版年份过滤书籍:

# 查询参数适用于资源过滤(如?status=active`)和分页控制(如?page=2&size=10),以问号(?)后追加的形式出现在URL中# 例如 /books?genre=fiction&year=2010# 添加查询参数@app.get("/books")# 此时year可选,None明确表示参数可缺失asyncdefread_books(year:int=None):ifyear:return{"year":year,"books":["Book 1","Book 2"]}return{"books":["All Books"]}

模型定义

基础模型
frompydanticimportBaseModelclassBook(BaseModel):# 每个字段都有类型声明title:strauthor:stryear:int
请求体
# 定义请求体frommodelsimportBook# Pydantic模型也用于请求体的结构定义# 当用户向 /book 发送包含JSON数据的POST请求时,FastAPI会自动解析并验证数据是否符合Book模型。若数据无效,将返回自动化的错误响应@app.post("/book")asyncdefcreate_book(book:Book):returnbook
# 高级验证功能frompydanticimportBaseModel,FieldclassBook(BaseModel):# 最小长度1个字符 最大长度100 个字符title:str=Field(...,min_length=1,max_length=100)author:str=Field(...,min_length=1,max_length=50)# 大于1900 小于2100year:int=Field(...,gt=1900,lt=2100)
响应模型
frompydanticimportBaseModel# 定义响应模型# 响应模型分离设计遵循最小权限原则,避免意外泄露敏感字段classBookResponse(BaseModel):title:strauthor:str# /allbooks GET端点需要返回图书列表,但仅包含书名和作者@app.get("/allbooks")# list[BookResponse] 表示使用BookResponse模型处理响应 保证响应只有两个属性asyncdefread_all_books()->list[BookResponse]:return[{"title":"1984","author":"George Orwell"},{"title":"The Great Gatsby","author":"F. Scott Fitzgerald"},]
# 在端点装饰器参数中指定响应类型# response_model参数具有更高优先级@app.get("/allbooks",response_model=list[BookResponse])asyncdefread_all_books():# 端点实现内容

异常处理

Http错误处理
fromfastapiimportFastAPI,HTTPExceptionfromstarlette.responsesimportJSONResponse# http_exception_handler函数将处理所有`HTTPException`错误。@app.exception_handler(HTTPException)asyncdefhttp_exception_handler(request,exc):returnJSONResponse(status_code=exc.status_code,content={"message":"Oops! Something went wrong"},)
# 测试用端点 显式抛出HTTP错误响应@app.get("/error_endpoint")asyncdefraise_exception():raiseHTTPException(status_code=400)
验证错误处理
importjsonfromfastapiimportRequest,statusfromfastapi.exceptionsimportRequestValidationErrorfromfastapi.responsesimportPlainTextResponse# 捕获所有RequestValidationError错误 并返回包含错误详情的纯文本响应@app.exception_handler(RequestValidationError)asyncdefvalidation_exception_handler(request:Request,exc:RequestValidationError):returnPlainTextResponse("This is a plain text response:"f" \n{json.dumps(exc.errors(),indent=2)}",status_code=status.HTTP_400_BAD_REQUEST,)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询