文章目录
1. File 参数
2. 多文件上传
3. 请求表单与文件
4. 处理错误
5. 自定义响应头
6. 自定义异常处理器
7. 覆盖默认异常处理器
8. 使用 RequestValidationError 的请求体
9. 复用 FastAPI 异常处理器
10. 路径操作参数配置
10.1 status_code,tags
10.2 summary,description
10.3 response description
10.4 deprecated 废除
11. jsonable_encoder() 转换
1. File 参数
from fastapi import FastAPI, Form, File, UploadFile app = FastAPI() @app.post("/files/") async def create_file(file: bytes = File(...)): return {"file_size": len(file)} @app.post("/uploadfile/") async def create_upload_file(file: UploadFile = File(...)): contents = await file.read() return {"filename": file.filename}
bytes 形式, 文件的所有内容都存储在内存里,适用于小型文件
很多情况下,UploadFile 更好用
1.存储在内存里的文件超出上限,FastAPI 会将其存入磁盘,大型文件不会用尽所有内存
2.可获取上传文件的元数据
3.自带 file-like async 接口
在 async 路径操作函数 内,要用以下方式读取文件内容:
contents = await myfile.read()
在普通 def 路径操作函数 内,则可以直接访问 UploadFile.file
contents = myfile.file.read()
2. 多文件上传
List[bytes], List[UploadFile]
from fastapi import FastAPI, Form, File, UploadFile from fastapi.responses import HTMLResponse from typing import List app = FastAPI() @app.post("/files/") async def create_files(files: List[bytes] = File(...)): return {"file_sizes": [len(file) for file in files]} @app.post("/uploadfiles/") async def create_upload_files(files: List[UploadFile] = File(...)): return {"filenames": [file.filename for file in files]} @app.get("/") async def main(): content = """ <body> <form action="/files/" enctype="multipart/form-data" method="post"> <input name="files" type="file" multiple> <input type="submit"> </form> <form action="/uploadfiles/" enctype="multipart/form-data" method="post"> <input name="files" type="file" multiple> <input type="submit"> </form> </body> """ return HTMLResponse(content=content)
3. 请求表单与文件
- FastAPI 支持同时使用 File 和 Form 定义文件和表单字段
@app.post("/f/") async def create_file( file1: bytes = File(...), file2: UploadFile = UploadFile(...), token: str = Form(...) ): return { "file_size": len(file1), "token" : token, "file_content_type" : file2.content_type }
- 可在一个路径操作中声明
多个 File
与Form
参数,但不能同时声明要接收JSON 的 Body
字段。因为此时请求体的编码为multipart/form-data
,不是application/json
4. 处理错误
- raise
HTTPException()
from fastapi import FastAPI, HTTPException app = FastAPI() items = {"foo" : "The Foo items"} @app.get("/items/{item_id}") async def read_item(item_id: str): if item_id not in items: raise HTTPException(status_code=404, detail="item not found!") return {"item" : items[item_id]}
触发 HTTPException 时,可以用参数 detail 传递任何能转换为 JSON 的值,不仅限于 str
。还支持传递 dict、list
等数据结构
INFO: Started server process [12136] INFO: Waiting for application startup. INFO: Application startup complete. INFO: 127.0.0.1:3229 - "GET /items/foo HTTP/1.1" 200 OK INFO: 127.0.0.1:3240 - "GET /items/bar HTTP/1.1" 404 Not Found
- 使用 dict 形式的 detail 参数也可以,只要可以被转为 JSON 即可
HTTPException(status_code=404, detail={"msg":"item not found!", "name":["michael","ming"]})
5. 自定义响应头
HTTPException(headers=xxx)
@app.get("/items-header/{item_id}") async def read_item_header(item_id: str): if item_id not in items: raise HTTPException( status_code=404, detail="Item not found", headers={"X-Error": "There goes my error!!!"}, ) return {"item": items[item_id]}
6. 自定义异常处理器
- 自定义异常类
- 编写 handler
@app.exception_handler(要处理的异常类)
from fastapi import FastAPI, Request from fastapi.responses import JSONResponse class MichaelException(Exception): def __init__(self, name: str): self.name = name app = FastAPI() @app.exception_handler(MichaelException) async def michael_exception_handler(request: Request, exec: MichaelException): return JSONResponse( status_code=408, content = {"msg": "哦,{}出错了".format(exec.name)}) @app.get("/test/{name}") async def test(name: str): if name == "yoyo": raise MichaelException(name) return {"test_name" : name}
7. 覆盖默认异常处理器
from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse app = FastAPI() @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id == 3: raise HTTPException(status_code=418, detail="3 is not a good number") return {"item_id" : item_id}
- 更改
RequestValidationError
错误的处理 handler
from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse app = FastAPI() @app.exception_handler(RequestValidationError) async def valid_excep_handler(req, exec): return PlainTextResponse(str(exec), status_code=400) @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id == 3: raise HTTPException(status_code=418, detail="3 is not a good number") return {"item_id" : item_id}
- 同样的,处理
HTTPException
的 handler,自定义处理
from starlette.exceptions import HTTPException as StarletteHTTPException # 跟 fastapi 的 HTTPException 一样 @app.exception_handler(StarletteHTTPException) async def http_exception_handler(req, exc): return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
or
@app.exception_handler(HTTPException) async def http_exception_handler(req, exc): return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
8. 使用 RequestValidationError 的请求体
RequestValidationError
包含其接收到的 无效数据请求的 body 。可以用这个请求体生成日志、调试错误,并返回给用户
from fastapi import FastAPI, Request, status from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from pydantic import BaseModel app = FastAPI() @app.exception_handler(RequestValidationError) async def valid_exception_handler(req: Request, exc: RequestValidationError): return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}), ) class Item(BaseModel): title: str size: int @app.post("/items/") async def create_item(item: Item): return item
9. 复用 FastAPI 异常处理器
- 在自定义处理完异常之后,还可以继续使用 默认的异常处理器
from fastapi import FastAPI, HTTPException from fastapi.exception_handlers import http_exception_handler, request_validation_exception_handler from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException app = FastAPI() @app.exception_handler(StarletteHTTPException) # 自定义处理异常 async def custom_http_exception_handler(req, exc): print(f"OMG! An HTTP error!: {repr(exc)}") return await http_exception_handler(req, exc) # 再调用自带的异常处理器 @app.exception_handler(RequestValidationError) async def validation_exception_handler(req, exc): print(f"OMG! The client sent invalid data!: {exc}") return await request_validation_exception_handler(req, exc) @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id == 3: raise HTTPException(status_code=418, detail="Nope! I don't like 3.") return {"item_id": item_id}
INFO: 127.0.0.1:9594 - "GET /items/abc HTTP/1.1" 422 Unprocessable Entity OMG! The client sent invalid data!: 1 validation error for Request path -> item_id value is not a valid integer (type=type_error.integer) INFO: 127.0.0.1:8106 - "GET /items/abc HTTP/1.1" 422 Unprocessable Entity INFO: 127.0.0.1:10417 - "GET /items/1 HTTP/1.1" 200 OK OMG! An HTTP error!: HTTPException(status_code=418, detail="Nope! I don't like 3.") INFO: 127.0.0.1:10417 - "GET /items/3 HTTP/1.1" 418
10. 路径操作参数配置
10.1 status_code,tags
tags = [字符串]
,将反映到 文档中
from typing import Optional, Set from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None tags: Set[str] = [] @app.post("/items/", response_model=Item, tags=["items, ming"], status_code=201) async def create_item(item: Item): return item @app.get("/items/", tags=["items"]) async def read_items(): return [{"name": "Foo", "price": 42}] @app.get("/users/", tags=["michael"]) async def read_users(): return [{"username": "johndoe"}]
10.2 summary,description
@app.post("/items/", response_model=Item, tags=["items, ming"], status_code=201, summary="创建item", description="描述item的一些信息" ) async def create_item(item: Item): return item
description
也可以由 多行注释直接生成,支持 MD 格式
@app.post("/items/", response_model=Item, tags=["items, ming"], status_code=201, summary="创建item", ) async def create_item(item: Item): ''' 多行注释 --> description - **name**: each item must have a name - **description**: a long description - **price**: required - **tax**: if the item doesn't have tax, you can omit this - **tags**: a set of unique tag strings for this item ''' return item
10.3 response description
response_description
参数
@app.post("/items/", response_model=Item, tags=["items, ming"], status_code=201, summary="创建item", response_description="响应的描述" ) async def create_item(item: Item): ''' 多行注释 --> description,支持 MD 格式 ## 1. 标题 - **name**: each item must have a name - **description**: a long description - **price**: required - **tax**: if the item doesn't have tax, you can omit this - **tags**: a set of unique tag strings for this item ''' return item
10.4 deprecated 废除
deprecated
@app.get("/users/", tags=["michael"], deprecated=True) async def read_users(): return [{"username": "johndoe"}]
11. jsonable_encoder() 转换
jsonable_encoder()
将pydantic
模型等数据结构 转换为 与json
兼容的格式(dict, list
等)
from datetime import datetime from typing import Optional from fastapi import FastAPI from fastapi.encoders import jsonable_encoder from pydantic import BaseModel fake_db = {} class Item(BaseModel): title: str timestamp: datetime description: Optional[str] = None app = FastAPI() @app.put("/items/{id}") def update_item(id: str, item: Item): json_data = jsonable_encoder(item) fake_db[id] = json_data return fake_db
这个例子把 Pydantic model
转换为 dict
, 把 datetime
转换为 str