前言
性能极高,可与 NodeJS, Go 媲美。(得益于Starlette和Pydantic)。
Starlette 是一个轻量级 ASGI 框架/工具包。它非常适合用来构建高性能的 asyncio 服务,并支持 HTTP 和 WebSockets。
官方网址:Starlette
Pydantic 是一个使用Python类型提示来进行数据验证和设置管理的库。Pydantic定义数据应该如何使用纯Python规范用并进行验证。
官方网址:https://pydantic-docs.helpmanual.io/
简单易懂,易于上手
1、设计的初衷就是易于学习和使用。不需要阅读复杂的文档。
2、良好的编辑器支持。支持自动完成,可以花更少的时间用于调试。
3、代码复用性强。
4、方便的 API 调试,生成 API 文档。
5、能提高开发人员两到三倍的开发速度。减少40%的人为出错几率。
一、基础环境的安装
Python 3.6+
FastAPI 站在这些巨人的肩膀上
- Starlette :web部分
- Pydantic :数据部分
可选依赖项:
Pydantic需要:
- ujson - 比较快的 JSON 解析.
- email_validator - email 校验.
Starlette需要:
- requests -
TestClient 需要
. - aiofiles -
FileResponse
或者StaticFiles 需要
. - jinja2 - 缺省模板配置需要.
- python-multipart - 表单解析需要.
- itsdangerous -
SessionMiddleware
支持需要. - pyyaml - Starlette's
SchemaGenerator
支持需要. - graphene -
GraphQLApp
支持需要. - ujson -
UJSONResponse 需要
.
FastAPI / Starlette需要:
- uvicorn - 加载和服务程序需要.
- orjson -
ORJSONResponse 需要
.
1.2、安装
1、pip install fastapi
2、我们需要一个ASGI服务器,可以使用 Uvicorn 或 Hypercorn。
pip install uvicorn
1.3 第一fastapi小案例
如图所示,本文所有的案例代码写在了casedemo中,并通过运行主要文件app.py文件实现功能的访问。代码如下所示:
案例1代码:
from fastapi import APIRouter
router = APIRouter()
# 这是我写的第一个测试用例。测试用例1
@router.get("/") #使用路由的方式实现了对/的请求
def read_root():return {"Hello": "World"}@router.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):return {"item_id": item_id, "q": q}
app.py主文件
from fastapi import FastAPI
import uvicorn
from casedemo import demo1_casedemo1app = FastAPI()
app.include_router(demo1_casedemo1.router)
# 运行 FastAPI
if __name__ == "__main__":uvicorn.run(app, host="0.0.0.0", port=8888)
启动的效果图如图所示
1.4 交互式API文档
我们访问以下两个地址,可获取自动生成的交互式API文档,并且当代码改动时文档会自动更新。方便我们的开发调试。
1、http://127.0.0.1:8000/docs (基于 Swagger UI)
2、http://127.0.0.1:8000/redoc (基于 ReDoc)
1.5 查询参数
声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数
@router.get("/demo2/read_items/")
async def read_item(skip: int = 0, limit: int = 10):return fake_items_db[skip : skip + limit]
二、Pydantic做类型强制检查
FastAPI 基于 Pydantic
,Pydantic
主要用来做类型强制检查。参数赋值,不符合类型要求就会抛出异常。
对于 API 服务,支持类型检查非常有用,会让服务更加健壮,也会加快开发速度,因为开发者再也不用自己写一行一行的做类型检查。
我们用纯粹的,经典的Python来定义数据,用Pydantic来校验数据。
官方文档地址:https://pydantic-docs.helpmanual.io/
安装
pip install pydantic
2.1 使用案例(BaseModel方式)
class User(BaseModel):id: intname: Optional[str] = "jack"signup_timestamp: datetime = Nonefriends: List[int] = []
观察到:
- id 要求必须为 int
- name 要求必须为 str, 且有默认值
- signup_timestamp 要求为 datetime, 默认值为 None
- friends 要求为 List,元素类型要求 int, 默认值为 []
2.2 使用 Query 作为默认值
我们打算添加约束条件:即使 q 是可选的,但只要提供了该参数,则该参数值不能超过50个字符的长度。
具体可以查阅文献:FastAPI教程 查询参数和字符串校验_w3cschool
@app.get("/items/")
async def read_items(q: Optional[str] = Query(None, max_length=50)):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results
2.3 完整的案例
from pydantic import ValidationError
from datetime import datetime
from typing import List
from pydantic import BaseModel
from typing import Optional
from fastapi import APIRouter, HTTPException # 使用路由
router = APIRouter()fake_items_db = [# 本部分模拟的是一个数据库信息的列表{"name": "Foo", "description": "A foo item", "price": 10.5},{"name": "Bar", "description": "A bar item", "price": 20.0},{"name": "Baz", "description": "A baz item", "price": 30.0}
]
class User(BaseModel):id: intname: Optional[str] = "jack"signup_timestamp: datetime = Nonefriends: List[int] = []class UserLogin(BaseModel):username: strpassword: strclass Item(BaseModel):# 当你创建 Item 类的实例时,Pydantic 会自动验证传入的数据是否符合类定义的类型约束。name:strdescription: Optional[str] = None'''description: Optional[str] = Nonedescription 是一个可选的字符串类型字段,使用了 Optional[str]。Optional[str] 表示这个字段可以是一个字符串类型,也可以是 None。如果在创建 Item 实例时未提供 description,默认值为 None。'''price: floattax: Optional[float] = None@router.post("/demo2/items/")
async def create_item(item: Item):# print(item.dict())# print(fake_items_db)if item.name in fake_items_db:# print(item.name)# print(item)raise HTTPException(status_code=200, detail="Item already exists")fake_items_db.append(item)# print(fake_items_db)return fake_items_db@router.post("/demo2/logindemo2")
async def userlogin_requestlogin_demo2(userlogin: UserLogin): # 接收 JSON 或表单数据if userlogin.username == "admin" and userlogin.password == "admin":print("当前进入到userlogin_requestlogindemo2函数中,用户名和密码正确")return {"status": 200, "msg": "登录成功"}else:print("当前进入到userlogin_requestlogindemo2函数中,用户名和密码错误")raise HTTPException(status_code=401, detail="用户名或密码错误")@router.post("/demo2/userdemo/")
async def demo2_userdemo(user:User):print(user)return user@router.get("/demo2/read_items/")
async def read_item(skip: int = 0, limit: int = 10):return fake_items_db[skip : skip + limit]
三、路径参数
3.1路径参数声明
我们可以用以下的方式来声明URL路径参数。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):return {"item_id": item_id}
这里,路径参数item_id的值会直接作为实参item_id传递给函数read_item。
运行这个示例并访问:http://127.0.0.1:8000/items/foo,结果如下:
{"item_id":"foo"}
3.2路径参数的变量类型
基于标准的Python类型注释,你可以在路径函数中声明路径参数的变量类型
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):return {"item_id": item_id}
这里,item_id
被声明为int类型。
运行代码并访问 http://127.0.0.1:8000/items/3,你会得到结果:
{"item_id":3}
注意这里函数接收到的数据是int类型的3,而不是字符串"3"。
也就是说,借助类型声明,FastAPI可以对Request内容自动进行数据解析和数据转换。
3.4 数据校验
如果访问 http://127.0.0.1:8000/items/foo,你会看到如下的错误:
{"detail": [{"loc": ["path","item_id"],"msg": "value is not a valid integer","type": "type_error.integer"}]
}
因为这里路径参数item_id的值是"foo",无法转换成int。
如果提供给item_id一个float类型的值,如4.2,也会发生类似的错误。
因此借助类型声明,FastAPI给了你自动进行数据校验的能力。返回结果里清晰的指出了具体错误内容,这非常有利于你的代码开发和调试。
所有的数据校验能力都是在Pydantic的底层支持下得以实现的。你可以利用通用的数据类型如str、float、bool或者其他更复杂的数据类型进行类型注释。
3.5 路径参数问题
在有些情况下,路径声明的顺序不同会影响访问结果,这里应该留意下。
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/me")
async def read_user_me():return {"user_id": "the current user"}
@app.get("/users/{user_id}")
async def read_user(user_id: str):return {"user_id": user_id}
这里路径 /users/me
应该在路径 /users/{user_id}
之前进行声明。否则,针对路径 /users/me 的访问就会被匹配到
/users/{user_id},因为
"me"
会被认为是路径参数 user_id
的值。
3.6 路径参数的预定义值
我们可以使用Python Enum来声明路径参数的预定义值。
from enum import Enum
class ModelName(str, Enum):alexnet = "alexnet"resnet = "resnet"lenet = "lenet"
这里,我们声明了一个类ModelName,它继承自str(这样限制了值的类型必须是字符串类型)和Enum。
这里也给出了ModelName类属性的值。
使用示例如下:
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):alexnet = "alexnet"resnet = "resnet"lenet = "lenet"
app = FastAPI()
@app.get("/model/{model_name}")
async def get_model(model_name: ModelName):if model_name == ModelName.alexnet:return {"model_name": model_name, "message": "Deep Learning FTW!"}if model_name.value == "lenet":return {"model_name": model_name, "message": "LeCNN all the images"}return {"model_name": model_name, "message": "Have some residuals"}
这时路径参数model_name的类型是我们定义的枚举类ModelName。
我们可以访问可交互式文档 http://127.0.0.1:8000/docs 来快速进行接口验证。
3.7 路径参数中包含文件路径
当路径参数中包含文件路径时,比如 /files/{file_path}
,我们可以用声明file_path类型的方式进行支持。
/files/{file_path:path}
这里:path
指出了file_path可以匹配任何文件路径。
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):return {"file_path": file_path}
3.8 路径操作的其他参数
我们可以在路径操作中添加其他参数,比如标签列表。
@app.post("/items/", response_model=Item, tags=["items"])
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=["users"])
async def read_users():return [{"username": "johndoe"}]
也可以添加summary和
description参数。
@app.post("/items/",response_model=Item,summary="Create an item",description="Create an item with all the information, name, description, price, tax and a set of unique tags", )
以及文档字符串。
@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(*, item: Item):"""Create an item with all the information:- **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
或者deprecated
参数表示该路径操作已过期。
@app.get("/elements/", tags=["items"], deprecated=True)
async def read_elements():return [{"item_id": "Foo"}]
四、请求参数
本部分已经在前面一二三中进行了展示,具体可以查看
FastAPI 基础学习(五) 请求参数 - 麦克煎蛋 - 博客园
五、Request Body
5.1 单个Request Body
FastAPI 基础学习(六) Request Body(I) - 麦克煎蛋 - 博客园
FastAPI 教程翻译 - 用户指南 5 - 请求主体_fastapi获取的body转成dict-CSDN博客
5.2 多个Request Body
我们可以同时声明多个Request Body参数。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Noneclass User(BaseModel):username: strfull_name: str = None@app.put("/items/{item_id}")
async def update_item(*, item_id: int, item: Item, user: User):results = {"item_id": item_id, "item": item, "user": user}return results
这两个参数(item、user)类型都是Pydantic数据模型。
*
用于强制在函数参数中使用关键字参数。这意味着在调用 update_item
时,item_id
必须以关键字的形式传递,而不能作为位置参数。这有助于提高代码的可读性和明确性。
六、参数附加信息
6.1 请求参数附加信息
FastAPI 基础学习(八) 参数附加信息 (一) - 麦克煎蛋 - 博客园
6.1.1 导入Query模块
from fastapi import Query
6.1.2基于Query模块声明缺省值
可选参数声明q: str = Query(None) # 等同于
q: str = None
缺省值参数声明q: str = Query("query") # 等同于 q: str = "query"
必选参数声明q: str = Query(...) # 等同于
q: str
6.1.3 添加附加信息
q: str = Query(None, max_length=50) # 限制q的最大长度不超过50
主要用于字符串参数的附加信息:min_length:最小长度
max_length:最大长度
regex:正则表达式
主要用于自动化文档的附加信息:title:参数标题
description:参数描述信息
deprecated:表示参数即将过期
特殊附加信息:alias:参数别名
例如:http://127.0.0.1:8000/items/?item-query=foobaritems
item-query并不是一个合法的Python变量名称,Python内部会对它进行转换,为了匹配到正确的参数变量我们就需要使用参数别名。
完整案例
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(None,alias="item-query",title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,max_length=50,regex="^fixedquery$",deprecated=True)
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results
6.2 请求参数列表
FastAPI基于Query模块可以支持请求参数列表,例如请求参数q可以在URL中出现多次:
http://localhost:8000/items/?q=foo&q=bar
对应代码如下:
from typing import List
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: List[str] = Query(None)):query_items = {"q": q}return query_items
返回结果内容为:
{"q": ["foo","bar"]
}
当然这里Query也支持请求参数列表的缺省值设置。
from typing import List
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: List[str] = Query(["foo", "bar"])):query_items = {"q": q}return query_items
我们也可以用list代替List[str],但这样的话FastAPI就无法校验列表内容了。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list = Query(None)):query_items = {"q": q}return query_items
6.3 路径参数附加信息
FastAPI 基础学习(九) 参数附加信息 (二) - 麦克煎蛋 - 博客园
6.3.1 导入Path模块
from fastapi import Path
6.3.2 添加附加信息
所有适用于请求参数的附加信息同样适用于路径参数,如title等。
item_id: int = Path(..., title="The ID of the item to get")
注意,路径参数在URL里是必选的,因此Path的第一个参数是...,即使你传递了None或其他缺省值,也不会影响参数的必选性。
6.3.3 代码示例
@router.get("/demo1/items/{item_id}")
async def read_items_demo1(q: str, item_id: int = Path(..., title="The ID of the item to get")
):results = {"item_id": item_id}if q:results.update({"q": q})return results
6.4 路径参数、请求参数顺序问题
6.4.1不带缺省值的参数应放在前面
如果把带有缺省值的参数放在了不带缺省值的参数前面,Python会发出运行警告。
因此在实际使用时,我们应当把不带缺省值的参数放在前面,无论这个参数是路径参数还是请求参数。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(..., title="The ID of the item to get")
):results = {"item_id": item_id}if q:results.update({"q": q})return results
FastAPI根据参数名称、类型以及声明方法(如Query、Path等等)来识别具体参数意义,并不关心参数顺序。
6.4.2 参数排序的技巧
通过传递 * 作为第一个参数,就解决了上面的参数顺序问题。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
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
Python并不会对 * 做任何操作。但通过识别 *,Python知道后面所有的参数都是关键字参数(键值对),通常也叫kwargs,无论参数是否有默认值。
6.5 数字类型参数的校正
借助Query、Path等模块你可以声明对字符串类型参数的校验,同样的,也可以实现对数字类型参数的校验功能。附加参数信息说明:gt: 大于(greater than)
ge: 大于或等于(greater than or equal)
lt: 小于(less than)
le: 小于或等于( less than or equal)
具体示例如下:
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*,item_id: int = Path(..., title="The ID of the item to get", gt=0, le=1000),q: str,
):results = {"item_id": item_id}if q:results.update({"q": q})return results
数字校验同样也适用于float类型的参数。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*,item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),q: str,size: float = Query(..., gt=0, lt=10.5)
):results = {"item_id": item_id}if q:results.update({"q": q})return results
七、Pydantic复杂模型
FastAPI 基础学习(十) Pydantic复杂模型 - 麦克煎蛋 - 博客园
7.1 Pydantic模型的附加信息
与前面讲过的Query、Path、Body类似,我们也可以为Pydantic模型添加附加信息,基于模块Field。
Field模块的参数与Query、Path、Body等相同。
from pydantic import BaseModel, Field
from fastapi import Body, FastAPI
from fastapi import APIRouter
router = APIRouter()class Item(BaseModel):name: strdescription: str = Field(None, title="The description of the item", max_length=300)price: float = Field(..., gt=0, description="The price must be greater than zero")tax: float = None@router.put("/demo3/items/{item_id}")
async def update_item(*, item_id: int, item: Item = Body(..., embed=True)):results = {"item_id": item_id, "item": item}return results
7.2 模型的嵌套
任意嵌套深度的Pydantic模型,我们都可以进行定义、校验、自动文档化和灵活使用。
比如list,dict,tuple,set等等。
from typing import Dict
from fastapi import FastAPIapp = FastAPI()@app.post("/index-weights/")
async def create_index_weights(weights: Dict[int, float]):return weights
7.3 复杂的数据类型
FastAPI 基础学习(十一) 复杂数据类型 - 麦克煎蛋 - 博客园
八、Cookie操作
FastAPI 基础学习(十二) Cookie操作 - 麦克煎蛋 - 博客园
基于Query、Path等模块同样的模式,我们可以利用Cookie模块来声明cookies。Cookie是Query、Path的姐妹类,它们都继承自Param类。同样我们也可以便捷的定义Cookie模块的参数信息。
九、Header操作
FastAPI 基础学习(十三) Header操作 - 麦克煎蛋 - 博客园
基于Query、Path、Cookie等模块同样的模式,我们可以利用Header模块来声明headers。我们可以用定义其他模块参数同样的方式,便捷的定义Header模块的参数信息。
Header是Query、Path、Cookie的姐妹类,它们都继承自Param类。
十、Response自定义状态码
FastAPI 基础学习(十四) Response自定义状态码 - 麦克煎蛋 - 博客园
十一、直接使用Request
参考文献
FastAPI 基础学习(一)概述 - 麦克煎蛋 - 博客园
FastAPI 基础学习(二)开发环境安装 - 麦克煎蛋 - 博客园
FastAPI 基础学习(三) Pydantic 做类型强制检查 - 麦克煎蛋 - 博客园
FastAPI教程 查询参数和字符串校验_w3cschool
FastAPI 基础学习(四) 路径参数 - 麦克煎蛋 - 博客园
FastAPI 基础学习(五) 请求参数 - 麦克煎蛋 - 博客园
FastAPI 基础学习(七) Request Body(II) - 麦克煎蛋 - 博客园
FastAPI 基础学习(八) 参数附加信息 (一) - 麦克煎蛋 - 博客园
FastAPI 基础学习(九) 参数附加信息 (二) - 麦克煎蛋 - 博客园
FastAPI 基础学习(十) Pydantic复杂模型 - 麦克煎蛋 - 博客园
FastAPI 基础学习(十一) 复杂数据类型 - 麦克煎蛋 - 博客园
FastAPI 基础学习(十二) Cookie操作 - 麦克煎蛋 - 博客园
FastAPI 基础学习(十三) Header操作 - 麦克煎蛋 - 博客园
FastAPI 基础学习(十四) Response自定义状态码 - 麦克煎蛋 - 博客园
FastAPI 基础学习(十五) 直接使用Request - 麦克煎蛋 - 博客园