目录
编辑
1、基本介绍
2、运行方式
3、创建接口步骤
4、自动生成API文档
4.1 交互式API文档
4.2 备用API文档
5、FastApi 执行顺序
6、Python-Pydantic库
6.1 BaseModel模型
6.2 请求体 + 路径参数 + 查询参数
7、Query/Path/Body/Field 参数(额外的校验) 与 字符串验证
8、typing类型注解
8.1 常用类型提示
8.2 Optional 可选类型
8.3 Union 联合类型
8.4 typing List
9、请求示例展示在接口文档中
10 、Cookie,Header参数
11 、响应模型 response_model
12 、响应状态码-使用 status_code 参数来声明
13 、Form表单数据
14 、上传文件-File, UploadFile
15 、处理错误-HTTPException
16、jsonable_encoder() 函数
17 、依赖注入-Depends
基于Python3.6+版本的、用于构建API现代的、高性能的web框架。FastAPI是建立在Pydantic和Starlette基础上的,Pydantic是一个基于Python类型提示来定义数据验证、序列化和文档的库。Starlette是一种轻量级的ASGI框架/工具包,是构建高性能Asyncio服务的理性选择。
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
在浏览器中请求 http://127.0.0.1:8000/docs ,显示交互式API文档, 自动交互式 API 文档(由 Swagger UI 提供),如下图:
在浏览器中请求 http://127.0.0.1:8000/redoc ,显示备用API文档, 备用的自动交互式文档(由 ReDoc 提供),如下图:
按照路径顺序匹配,默认匹配的是第一个带参数的路径
from pydantic import BaseModel
# schema基本定义方法
class Person(BaseModel):
name: str
# 基本的schema实例化方法-直接传入
p = Person(name="ABC123")
print(p.json())
>>> {"name": "ABC123"}
from pydantic import BaseModel
class User(BaseModel):
username: str
email: Optional[str] = None
full_name: Optional[str] = None
status: Optional[bool] = None
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
"""
函数参数将依次按如下规则进行识别:
● 如果在路径中也声明了该参数,它将被用作路径参数(例如:item_id)。
● 如果参数属于单一类型(比如 int、float、str、bool 等)它将被解释为查询参数(例如:q)。
● 如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体(例如:item)。
"""
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: Union[str, None] = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
from fastapi import FastAPI, Query
# m是可选参数,参数长度2-10,以name开头
@app.get("/update_items/")
# 当使用 Query 且需要声明一个值是必需的时,可以将 ... 用作第一个参数值
# def update_items(m: Optional[str] = Query(..., max_length=10,min_length=2,regex="^name")):
def update_items(m: Optional[str] = Query(None, max_length=10,min_length=2,regex="^name")):
results = {"items": [{"oneid": "北京"}, {"two": "上海"}]}
if m:
results.update({"上海": m})
return results
"""
声明数值校验:
gt:大于(greater than)
ge:大于等于(greater than or equal)
lt:小于(less than)
le:小于等于(less than or equal)
regex正侧参数的写法:
以 ^ 符号开头
以 $ 符号结尾
使用省略号(...)声明必需参数
"""
# 需求:id大于5才能返回
@app.get("/id")
async def read_id(*, id: int = Query(..., ge=5, ), q_value: str):
results = {"message": f"{q_value}"}
if id:
results.update({"new_value": q_value})
return results
"""
● 传递 * 作为函数的第一个参数,
● 如果单独出现星号 *,
● 则星号 * 后的参数必须用关键字传入
"""
@app.get("/items/{item_id}")
# 使用 Path 为路径参数声明相同类型的校验和元数据
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
"""
●嵌入单个请求体参数,期望一个拥有 item 键并在值中包含模型内容的 JSON,
●返回如下:
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
●可以使用一个特殊的 Body 参数 embed : item: Item = Body(embed=True)
"""
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
results = {"item_id": item_id, "item": item}
return results
# 通过get方式在URL路径中接收请求参数
@app.get("/items/{item_id}")
async def read_root1(item_id: int = Path(..., gt=0)):
return {"item_id": item_id}
# 虽然是通过post方式提交请求,但item_id仍然是在URL中通过?item_id进行请求
@app.post("/items")
async def read_root2(item_id: int = Query(..., ge=1)):
return {"item_id": item_id}
# post方式提交请求,但item_id仍然是在URL中通过?item_id进行请求
@app.post("/items")
async def read_root2(
item_id: int = Body(..., ge=1, embed=True),
item_name: str = Body(None, max_length=20)):
return {"item_id": item_id, "item_name": item_name}
typing —— 类型注解支持 — Python 3.11.0 文档
前两行小写的不需要 import,后面三行都需要通过 typing 模块 import
# 下面的函数接收与返回的都是字符串,注解方式如下:
def items(name: str) -> str:
return "hello" + name
print(items("123"))
>>>hello123
(声明a :Optional[int] = None) ,参数除了给定的默认值外还可以是None(作用是让编译器识别到该参数有一个类型提示,可以使指定类型,也可以是None,且参数是可选非必传的)。注意 Optional[] 里面只能写一个数据类型。即 Optional[X] 等价于 Union[X, None]
typing —— 类型注解支持 — Python 3.11.0 文档
# 参数end是可选择的参数,有end就返回需求的,没有end返回所有的
@app.get("/params")
async def read_param(start: int = 0, end: Optional[int] = None):
if end:
return data[start:end]
return data[start::]
# Union[int, None] 表示既可以是 int,也可以是 None。没有顺序的说法
python 3.6 及以上版本,需要导入typing中的Union
from typing import Union
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: Union[int, None] = None
python 3.9 及以上版本 ,不需要导入typing中的Union
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: int | None = None
from typing import Optional
import uvicorn
from fastapi import FastAPI, Body
from typing import List, Tuple
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(
list_: List[int] = Body(...),
tuple_: Tuple[int] = Body(...),
):
results = {"list_": list_, "tuple_": tuple_}
return results
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8080)
使用Config 和 schema_extra 为Pydantic模型声明一个简单的示例
import uvicorn
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel, Field
from typing import Union
class Items(BaseModel):
name: str
desc: Optional[str] = None
price: float
tax: Optional[float] = None
# 接口请求示例展现在接口文档中
"""使用Config 和 schema_extra 为Pydantic模型声明一个简单的示例"""
class Config:
schema_extra = {
"example": {
"name": "书名",
"price": 20,
"decs": "描述信息",
"tax": 0.5
}
}
@app.post("/items1")
async def retrun_item(item: Items):
results = {"item": item}
return results
"""
通过工厂函数,增加example参数
注意:传递的额外参数不会添加任何验证,只会添加注释,用于文档的目的
"""
class Item(BaseModel):
name: str = Field(example="Foo")
description: Union[str, None] = Field(default=None, example="A very nice Item")
price: float = Field(example=35.4)
tax: Union[float, None] = Field(default=None, example=3.2)
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
if __name__ == '__main__':
uvicorn.run(app, host="127.0.0.1", port=8000)
"""
header的必须有token且token必须是456,没有返回无权限,
cookie必须有一个name,且等于123,否则返回认证失败
"""
from typing import Optional
from fastapi import Cookie, FastAPI,Header
app = FastAPI()
@app.get("/items/")
def read_items(name: Optional[str] = Cookie(None),
token: Optional[str] = Header(None)):
if token is None or token!='456':
return '无权限'
if name is None or name !="123":
return "认证失败"
class UserIn(BaseModel):
username: str
password: str
email: str
full_name: Optional[str] = None
class Userout(BaseModel):
username: str
email: str
full_name: Optional[str] = None
@app.post("/user/", response_model=Userout)
def create_user(user: UserIn):
return user
https://fastapi.tiangolo.com/zh/tutorial/response-status-code/
import uvicorn
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/",status_code=200)
def login(username: str = Form(...), password: str = Form(...)):
if password == "123456":
return {"username": username}
return "密码错误"
# 注册用户,username长度8-16位,password长度6-16位,符合需求返回对应username
@app.post("/register", status_code=200)
async def register(username: str = Form(..., min_length=8, max_length=16, regex='^[a-zA-Z]'),
password: str = Form(..., min_length=8, max_length=16, regex='^[0-9]')):
return {"username": username}
if __name__ == '__main__':
uvicorn.run(app, host="127.0.0.1", port=8000)
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
def create(file: bytes = File(...)):
return {"file_size": len(file)}
@app.post("/uploadfile/")
def upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"test": "浅说测试开发"}
@app.get("/items/{item_id}")
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]}
https://fastapi.tiangolo.com/zh/tutorial/encoder/#__tabbed_1_2
from fastapi import Depends
# 定义依赖项函数
def com_methods(q: Optional[str] = None, a: int = 0, b: int = 10):
return {"q": q, "a": a, "b": b}
@app.get("/com_item1")
def com_item1(item1: dict = Depends(com_methods)):
return item1
@app.post("/com_item2")
def com_item2(item2: dict = Depends(com_methods)):
return item2
"""
class CommonQueryParams:
类实现依赖注入2中写法:
1.commons: CommonQueryParams = Depends(CommonQueryParams)
2.commons: CommonQueryParams = Depends()
"""
# 全局都需要校验token
from fastapi import FastAPI,Header, HTTPException,Depends
def verify_token(token: str = Header(...)):
if token!="asdfghjkl":
raise HTTPException(status_code=400, detail="Token header invalid")
app = FastAPI(dependencies=[Depends(verify_token)])