Fastapi学习总结(上)
2021/4/17 10:30:28
本文主要是介绍Fastapi学习总结(上),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录- 简介
- 安装
- HelloWord
- 编写脚本
- 启动服务
- 浏览器访问接口
- 查看交互式API文档
- 查看可选的api文档
- url路径参数
- 基本用法
- 枚举类型参数
- 包含路径的路径参数
- 查询参数
- 请求体
- 使用pydantic声明请求体
- pydantic请求体 + 路径参数 + 查询参数
- 不使用pydantic声明请求体-Body
- 查询参数的额外校验-Query
- 查询参数列表
- 路径参数的额外校验-Path
- Pydantic的额外校验-Field
- 其他数据类型
- Cookie
- Header
- 响应
- 基本使用
- 其他响应参数选择
- 响应模型类-Union
- 响应模型类-List
简介
FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。
关键特性:
- 快速:可与 NodeJS 和 Go 比肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。
- 高效编码:提高功能开发速度约 200% 至 300%。*
- 更少 bug:减少约 40% 的人为(开发者)导致错误。*
- 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
- 简单:设计的易于使用和学习,阅读文档的时间更短。
- 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。
- 健壮:生产可用级别的代码。还有自动生成的交互式文档。
- 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。
安装
uvicorn
为ASGI服务器
pip install fastapi pip install uvicorn
HelloWord
编写脚本
创建一个名为hello_world.py
的文件
from fastapi import FastAPI # 创建fastapi的应用, 自定义名为app app = FastAPI() # url+http方法+执行函数 @app.get('/') async def hello_world(): return {'hello': 'world'}
启动服务
通过uvicorn
启动服务, 其中--reload
是可选参数, 代码修改后会自动重启服务
uvicorn hello_world:app --reload
启动结果如下则说明启动成功, 默认是在8000端口启动的, 地址为http://127.0.0.1:8000
$ uvicorn hello_world:app --reload INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: Started reloader process [189] using statreload INFO: Started server process [191] INFO: Waiting for application startup. INFO: Application startup complete.
浏览器访问接口
通过浏览器访问http://127.0.0.1:8000
, 可以看到返回结果
查看交互式API文档
FastAPI会自动生成交互式API文档, 通过网址http://127.0.0.1:8000/docs
访问, 这个可以方便开发人员更好地调试api, 也可以在文档上写入相关的注释, 方便前后端开发的对接
查看可选的api文档
除了上面的交互式api文档外, fastapi还提供了另一个文档, 通过http://127.0.0.1:8000/redoc
访问, 这个文档只能查看, 不能交互
url路径参数
基本用法
这里的路径参数指的是url访问资源的路径中的参数, 比如根据restful规范, 定义访问id为1的商品, 对应的url应该是/goods/1
, 那么如果想访问id为n的商品, url应该是/goods/n
, 这个n
就是路径参数.
在fastapi中定义路径参数的方法是:
@app.get('/items/{item_id}') async def get_item_by_id(item_id: int): return {'item_id': item_id}
-
item_id
即为路径参数名, 路径参数需要用大括号括起来, 且在执行函数get_item_by_id
中需要定义与路径参数同名的参数item_id
-
在定义函数参数
item_id
时, 用到了python3.6+
引入的"类型提示", 这样能够让代码和读维护起来更加方便, 编辑器也能够根据类型提供更多的支持, 推荐使用 -
有了类型提示后, 如果url传入的参数不能转换为int类型, 则fastapi会自动检测并返回错误信息, 如:
枚举类型参数
# 导入枚举模块 from enum import Enum # 定义枚举类 class BookType(str, Enum): java = "Java" python = "Python" c_plus = "C++" # 定义函数 @app.get('/books/{book_type}') async def get_book_type(book_type: BookType): result = {'book_type': book_type, 'message': ''} if book_type == BookType.java: result['message'] = "This is java" elif book_type == BookType.python: result['message'] = 'This is python' elif book_type == BookType.c_plus: result['message'] = 'This is c++' return result
- 定义枚举类
BookType
时需要继承str
和Enum
类, 表明枚举值都是字符串类型, 下面的类变量即为枚举变量和枚举值 - 参数
book_type
的类型注解为定义的枚举类BookType
包含路径的路径参数
@app.get('/files/{file_path:path}') async def get_file_path(file_path): return {'file_path': file_path}
- 在url中定义参数路径参数时, 如果该参数值本身也是一个路径, 为了不会混淆, 可以在url参数后面添加
:path
, 用来表示这个参数是一个路径格式
查询参数
查询参数指的是在url后面添加的以?
开头并以&
分隔的一系列键值对参数
在fastapi的函数中, 如果声明的参数不属于路径参数, 那么它就会被自动解释为查询参数
from typing import Optional @app.get('/items/') async def get_query_param(page: int, count: Optional[int] = 10): return {'page': page, 'count': count}
- 在函数
get_query_param
后定义了两个参数page
和count
, 这两个参数并没有对应的url路径参数, 因此fastapi会认为这两个参数是查询参数 - 其中
page
参数没有指定Optional
和默认值, 因此该参数被认为是必输的 count
参数指定了为Optional
, 可选的, 并给了默认值, 那么说明这个参数不是必填的, 如果没填, 就默认这个参数值为10
请求体
使用pydantic声明请求体
# 导入pydantic基本模型 from pydantic import BaseModel # 创建请求体的pydantic模型类 class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None @app.post('/items/') async def create_item(item: Item): # return item 可以直接返回pydantic类型的数据 # 也可以将pydantic转为字典, 然后做后续的逻辑处理 item_dict = item.dict() if item.tax: price_with_tax = item.price + item.tax item_dict['price_with_tax'] = price_with_tax return item_dict
- 在定义pydantic模型类时, 和声明查询参数一样, 可以设置Optional属性指定其是否为 必输字段
- 声明执行函数的参数
item
类型为上面定义的pydantic模型类 - fastapi会把传入的请求体作为json读取, 然后转为定义的pydantic模型类, 并校验数据的类型和是否必输
在fastapi的交互式文档的schemas
中, 可以看到定义的pydantic模型类, 上面指明了类型和是否必输
pydantic请求体 + 路径参数 + 查询参数
# 导入pydantic基本模型 from pydantic import BaseModel # 创建请求体的pydantic模型类 class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None @app.put('/items/{item_id}') async def update_book(item_id: int, item_type: str, item: Item): return {'path_param': {'item_id': item_id}, 'query_param': {'item_type': item_type}, 'req_body': item}
可以同时声明 路径参数+查询参数+请求体, fastapi会自动识别他们中的每一个并从正确的位置获取数据, 函数参数将依次按如下规则进行识别:
- 如果在路径中也声明了该参数,它将被用作路径参数。
- 如果参数属于单一类型(比如
int
、float
、str
、bool
等)它将被解释为查询参数。 - 如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体。
不使用pydantic声明请求体-Body
使用fastapi的Body
来定义单一的请求体参数, 在参数后面加上= Body(...)
来表示这个参数是请求体参数, 如下面的item_name
参数和item_price
参数, 但是这样只能一个一个定义请求体参数, 如果参数很多还是建议使用pydantic的模型类定义
from fastapi import Body @app.patch('/items/{item_id}') async def patch_book(item_id: int, item_type: str, item_name: str = Body(...), item_price: float = Body(...)): return {'path_param': {'item_id': item_id}, 'query_param': {'item_type': item_type}, 'req_body': {'item_name': item_name, 'item_price': item_price}}
请求为:
响应为:
查询参数的额外校验-Query
使用fastapi的Query类型, 在参数后面加上= Query()
, 如限制最长不能超过5位, Query(max_length=5)
from fastapi import Query @app.get('/query_items/') async def query_items(item_type: Optional[str] = Query(None, max_length=5)): result = {'items': [{'item_id': 1}, {'item_id': 2}]} if item_type: result['item_type'] = item_type return item_type
点击Query进入源码可以查看Query所接受的参数
def Query( # noqa: N802 default: Any, # 参数默认值 *, alias: Optional[str] = None, # 参数别名, 对应url中的查询参数名 title: Optional[str] = None, description: Optional[str] = None, gt: Optional[float] = None, # 参数值需大于... ge: Optional[float] = None, # 参数值需大于等于... lt: Optional[float] = None, # 参数值需小于... le: Optional[float] = None, # 参数值需小于... min_length: Optional[int] = None, # 参数最小长度... max_length: Optional[int] = None, # 参数最大长度... regex: Optional[str] = None, # 正则表达式限制... deprecated: Optional[bool] = None, # 是否已经失效 **extra: Any, ) -> Any:
- default: Query的第一个参数, 表示该查询参数的默认值, 如果设置了这个默认值的话, 那么就认为这个参数是可选参数(非必输), 相当于
Optional[type] = default
, 如果想要设置参数必输, 那么就把default值设为Query(...)
查询参数列表
from typing import List @app.get('/list_items/') async def get_list_items(q: Optional[List[str]] = Query(None)): return {'q': q}
访问urlhttp://127.0.0.1:8000/list_items/?q=1&q=2
, 返回结果:
{ "q": [ "1", "2" ] }
- 在参数中使用了List类型后, fastapi会将该参数解析为请求体, 因此如果想要该参数为查询参数, 那么需要显式地使用
Query
路径参数的额外校验-Path
与使用Query
声明查询参数类似, 路径参数可以使用Path
来声明
from fastapi import Path @app.get('/path_items/{item_name}') async def get_path_items(item_name: str = Path('Python', max_length=6)): return {'item_name': item_name}
Path的参数用法与Query
一样:
def Path( # noqa: N802 default: Any, *, alias: Optional[str] = None, title: Optional[str] = None, description: Optional[str] = None, gt: Optional[float] = None, ge: Optional[float] = None, lt: Optional[float] = None, le: Optional[float] = None, min_length: Optional[int] = None, max_length: Optional[int] = None, regex: Optional[str] = None, deprecated: Optional[bool] = None, **extra: Any, ) -> Any
Pydantic的额外校验-Field
与使用 Query
、Path
和 Body
在路径操作函数中声明额外的校验和元数据的方式相同,可以使用 Pydantic 的 Field
在 Pydantic
模型内部声明校验和元数据。需要导入pydantic
的Field
from pydantic import Field class MyItem(BaseModel): item_id: int item_name: str = Field(None, max_length=5) @app.post('/field_items/') async def get_field_items(my_item: MyItem): return my_item
其他数据类型
-
UUID
- 一种标准的 "通用唯一标识符" ,在许多数据库和系统中用作ID。
- 在请求和响应中将以
str
表示。
-
datetime.datetime
- 一个 Python
datetime.datetime
. - 在请求和响应中将表示为 ISO 8601 格式的
str
,比如:2008-09-15T15:53:00+05:00
.
- 一个 Python
-
datetime.date
- Python
datetime.date
. - 在请求和响应中将表示为 ISO 8601 格式的
str
,比如:2008-09-15
.
- Python
-
datetime.time
- 一个 Python
datetime.time
. - 在请求和响应中将表示为 ISO 8601 格式的
str
,比如:14:23:55.003
.
- 一个 Python
-
datetime.timedelta
- 一个 Python
datetime.timedelta
. - 在请求和响应中将表示为
float
代表总秒数。 - Pydantic 也允许将其表示为 "ISO 8601 时间差异编码"
- 一个 Python
-
frozenset
- 在请求和响应中,作为
set
对待:- 在请求中,列表将被读取,消除重复,并将其转换为一个
set
。 - 在响应中
set
将被转换为list
。 - 产生的模式将指定那些
set
的值是唯一的 (使用 JSON 模式的uniqueItems
)。
- 在请求中,列表将被读取,消除重复,并将其转换为一个
- 在请求和响应中,作为
-
bytes
- 标准的 Python
bytes
。 - 在请求和相应中被当作
str
处理。 - 生成的模式将指定这个
str
是binary
"格式"。
- 标准的 Python
-
Decimal
- 标准的 Python
Decimal
。 - 在请求和相应中被当做
float
一样处理
- 标准的 Python
from datetime import datetime, time, timedelta from uuid import UUID @app.put('/others/{item_id}') async def get_others( item_id: UUID, start_datetime: Optional[datetime] = Body(None), end_datetime: Optional[datetime] = Body(None), repeat_at: Optional[time] = Body(None), process_after: Optional[timedelta] = Body(None), ): start_process = start_datetime + process_after duration = end_datetime - start_datetime return { 'item_id': item_id, 'start_datetime': start_datetime, 'end_datetime': end_datetime, 'repeat_at': repeat_at, 'process_after': process_after, 'start_process': start_process, 'duration': duration, }
Cookie
声明 Cookie
参数的结构与声明 Query
参数和 Path
参数时相同。
第一个值是参数的默认值,同时也可以传递所有验证参数或注释参数,来校验参数:
from fastapi import Cookie @app.get('/cookies/') async def get_cookies(user_id: Optional[str] = Cookie(None)): return {'user_id': user_id}
Header
可以使用定义 Query
, Path
和 Cookie
参数一样的方法定义 Header 参数。
from fastapi import Header from typing import List @app.get('/headers/') async def get_headers(user_agent: Optional[str] = Header(None, convert_underscores=False), user_id: Optional[List[str]] = Header(None)): return {'user_agent': user_agent, 'user_id': user_id}
-
Header
比Query
,Path
和Cookie
多了一个自动转换的功能, 因为大多数标准的headers是用中划线-
连接的, 但是中划线-
不符合python中定义变量的规范, 因此默认情况下, fastapi会把函数中定义的Header
参数中的下划线_
自动替换成中划线-
, 然后去请求的headers中寻找对应的header -
同时, HTTP headers是大小写不敏感的, 所以在python中定义参数时无需考虑大写, 可以全部使用小写来命名, 例如http headers中的
User-Agent
, 对应fastapi函数的参数就可以写为user_agent
(大小写和中划线都可以转换) -
如果不想在某个参数上使用fastapi的自动转换功能, 可以设置
convert_underscores=False
来取消自动转换 -
Header
支持接收重复的headers, 可以通过List
类型来重复接收, 比如headers中有两个同名headeruser_id: foo user_id: bar
那么函数中的
user_id
就为:{ "user_id": [ "bar", "foo" ] }
响应
基本使用
在@app.method()
装饰器中使用response_model
参数来声明用于响应的模型
from pydantic import EmailStr # 请求体json模型 class UserIn(BaseModel): user_name: str pass_word: str email: Optional[EmailStr] = None full_name: Optional[str] = None # 响应体json模型 class UserOut(BaseModel): user_id: int user_name: str email: Optional[EmailStr] = None full_name: Optional[str] = None @app.post('/create_user/', response_model=UserOut) async def create_user(user: UserIn): user_dict = user.dict() user_dict['user_id'] = 1 return user_dict
-
pydantic
有自带的邮箱格式校验类型EmailStr
, 不过需要额外进行安装,pydantic
只会安装核心的功能pip install pydantic[email]
-
这里定义的响应json模型
UserOut
比UserIn
多了user_id
和少了pass_word
字段 -
视图函数中返回的
user_dict
中是包括了pass_word
字段的, 但是由于设置了response_model=UserOut
, fastapi会过滤掉其他的字段
其他响应参数选择
def post(... response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None, response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None, response_model_by_alias: bool = True, response_model_exclude_unset: bool = False, response_model_exclude_defaults: bool = False, response_model_exclude_none: bool = False, ...):
- response_model_include: 返回模型中需要包括哪些字段
- response_model_exclude: 返回模型中不能包括哪些字段
- response_model_exclude_unset: 设置为True的话那么响应模型中未显式设置具体值的字段则不会返回
响应模型类-Union
可以将一个响应声明为两种类型的 Union
,这意味着该响应将是两种类型中的任何一种。
from typing import Union class Student(BaseModel): student_name: str student_score: float class Teacher(BaseModel): teacher_name: str teacher_num: str items = { 'student': {'student_name': 'xiaoming', 'student_score': 90.3}, 'teacher': {'teacher_name': 'li', 'teacher_num': 'NO.123'} } @app.get('/person/', response_model=Union[Student, Teacher]) async def get_person(item_type: str): return items[item_type]
- 返回
Student
模型类或者Teacher
模型类
响应模型类-List
from typing import List class Student(BaseModel): student_name: str student_score: float students = [ {'student_name': 'xiaoming', 'student_score': 90.3}, {'student_name': 'xiaohong', 'student_score': 80.7} ] @app.get('/students/', response_model=List[Student]) async def get_students(): return students
- 返回的是
Student
模型类列表,List
中只能有一种模型类型
这篇关于Fastapi学习总结(上)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-27OpenFeign服务间调用学习入门
- 2024-12-27OpenFeign服务间调用学习入门
- 2024-12-27OpenFeign学习入门:轻松掌握微服务通信
- 2024-12-27OpenFeign学习入门:轻松掌握微服务间的HTTP请求
- 2024-12-27JDK17新特性学习入门:简洁教程带你轻松上手
- 2024-12-27JMeter传递token学习入门教程
- 2024-12-27JMeter压测学习入门指南
- 2024-12-27JWT单点登录学习入门指南
- 2024-12-27JWT单点登录原理学习入门
- 2024-12-27JWT单点登录原理学习入门