当前位置: 首页 > news >正文

(01)fastapi的基础学习——开启学习之路

前言

性能极高,可与 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"}]

也可以添加summarydescription参数。

@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 - 麦克煎蛋 - 博客园


http://www.mrgr.cn/news/53452.html

相关文章:

  • JavaScript (基础)
  • 【力扣打卡系列】滑动窗口与双指针(两数之和)
  • 优化漏洞扫描流程以保障企业数字化业务安全
  • 【AI学习】Mamba学习(八):HiPPO通用框架定义和方法
  • 小白学电路之电流镜仿真
  • OpenLayers:用于在 web 应用程序中创建互动地图
  • 目标检测最新SOTA模型D-FINE
  • 【分布式微服务云原生】《Redis 分布式锁的挑战与解决方案及 RedLock 的强大魅力》
  • UG NX12.0建模入门笔记:1.1 UG界面编辑
  • 【Gitee版】一篇教你如何快速入门git(详解)
  • Android 下通过触发 SIGTRAP 信号实现反调试
  • Broker 模式
  • 使用Git进行版本控制
  • TCP——Socket
  • 大型敏捷SAFe的关键职能最全解释
  • Python分析生存数据与截尾
  • Cisco Nexus N93108转换模式for Nxos to ACI mode失败案例
  • 笔试强训10.19
  • 银河麒麟V10系统+Windows10双系统启动顺序正确修改方法
  • 【动手学深度学习】7.5 批量规范化(个人向笔记)