文章目录
- 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() 转换
learn from https://fastapi.tiangolo.com/zh/tutorial/request-files/
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 JSONResponseclass MichaelException(Exception):def __init__(self, name: str):self.name = nameapp = 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 PlainTextResponseapp = 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 PlainTextResponseapp = 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 BaseModelapp = 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: strsize: int@app.post("/items/")
async def create_item(item: Item):return item
输入 非整数的 size ,得到报错 {"detail": exc.errors(), "body": exc.body}
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 StarletteHTTPExceptionapp = 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_idvalue 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, Setfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Optional[str] = Noneprice: floattax: Optional[float] = Nonetags: 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 Optionalfrom fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModelfake_db = {}class Item(BaseModel):title: strtimestamp: datetimedescription: Optional[str] = Noneapp = FastAPI()@app.put("/items/{id}")
def update_item(id: str, item: Item):json_data = jsonable_encoder(item)fake_db[id] = json_datareturn fake_db
这个例子把 Pydantic model
转换为 dict
, 把 datetime
转换为 str