FastAPI - 一款新型的 Python Web 框架(对比 Flask)
近日曾想尽办法为 Flask 实现 Swagger UI 文档功能,找到的实现方式基本上是 Flask + Flasgger, 记录在 Flask 应用集成 Swagger UI。然而不断的 Google 过程中偶然发现了一款集成了 Swagger UI 的比 Flask 还好的 Python Web 框架 -- FastAPI 。起初想要在标题中表达的意思大概是 Flask + Swagger = FastAPI, 后来发现 FastAPI 的闪亮点不仅如此,于是乎又找了些 Flask 与 FastAPI 对比的文章读一读,在文后附有链接。
本文不对 Flask 与 FastAPI 的各个方面对进行对比,本人兴趣依然还是在 FastAPI 的 Swagger UI 功能,以及与 Flask 的 Blueprint 类似的特性。如果要拿 Flask 与 FastAPI 比较的话,应该用 Flask 2.x, 因为它开始支持类似 @app.get 的装饰器,并引入了 async 路由函数。
Flask 是在 2010 年发布的,它构建于 WSGI(Python Web Server Gateway Interface) 之上的,产品环境中运行需与 uWSGI, Gunicorn 搭配,或用 mod_wsgi 模块与 Apache 集成。因发布较早,所以目前应该有较多的使用者。Flask 2.0 需要 Python 3.6+ 的支持,如果支持
FastAPI 发布于 2018 年,构建于 ASGI(Asynchronous Server Gateway Interface) 之上,在 IO 密集型的应用中有更优越的性能。生成环境中配合 ASGI 服务器,如 Uvicorn 或 Hypercorn. FastAPI 最为亮丽的特性是集成了 Swagger UI -- 外加一个福利 ReDoc。FastAPI 需 Python 3.6+ 版本。
FastAPI 构建在 Starlette 之上, 更多类似包见 awesome-asgi。它的请求/响应数据定义,项目配置,校验库是 Pydantic, OpenAPI 自动生成的 Model 也靠它。Pydantic 使用了更高效的 JSON 处理器 ujson, Starlette 的 ORJSONResponse 使用了更利害的 orjson。
毕竟是在开始学一个新的框架,还是从它的基本用法开始,途中会穿插与 Flask 的对比。
创建一个
注:以上两个函数前面可以加上
启动服务,用命令
访问服务
与 Flask 的对比
回顾一个 Flask 启动服务的方式有
然后像普通 Python 代码一样执行
注:uvicorn.run() 可接受更多的参数,相当于
同样把 main.py 当作普通 Python 程序来启动即可
同时注意到函数映射为 API 的名称,由于参数是带有类型提示的,所以在 SwaggerUI 中也能看到相应的类型,Optional 的类型没有 *required 标识。其他定制 Swagger UI 的工作需要时可细细研究,如 1) 文档描述,2) API 名称定义, 3) API 描述, 4) API 验证 - Basic, OAuth, Keys
如果是要从请求头里获取值,则在路由函数中定义如下参数:
这样在 SwaggerUI 和 Redoc 中也能看到该 API 需要从 HttpRequest Header 中获取
FastAPI 除了 SwaggerUI 外,还同时给了我们另一个选择,那就是 redoc, 访问 http://localhost:8000/redoc, 看到下面的界面
我也是头一回从 FastAPI 这里了解到还有一个 Redoc 的 Web API 文档工具,它和 SwaggerUI 类似,都是支持 Open API,但不能用 Redoc 来调用 API。
下面是 main.py 的全新内容
再查看 http://localhost:8000/docs 文档,看到 PUT /items/{item_id} 的界面如下
我们能看到请求与响应中的 Example Value 和 Schema 定义
梳理完 FastAPI 的自动 API 文档功能,进入下一个话题
每个目录中都可以放一个
app/internal/admin.py
app/routers/items.py
app/routers/users.py
在每个单独定义路由的文件里用到的是
app/main.py
这里实际上使用了三种定义路径前缀与 tags(API 分类) 的方式
现在运行(如果之前运行 uvicorn 没有出现异常,由于有 --reload 参数会自动刷新)
查看 SwaggerUI http://localhost:8000/docs

大家都是 FastAPI, 用
与 APIRouter 不同的是,它们有各自的 /docs 和 /redoc
访问 /redoc, /subapi/docs 也是类似的效果。
这种方式针对不同客户端开发不同的 API, 或者前后台可以注册为不同的子项目,这让不同组的 API 文档能够相互独立。
到目前为止我重点关注 FastAPI 的就是以上那两个特性:自动 API 文档与大程序多文件(Blueprint) 功能。其他的特性可查阅 FastAPI 的 Features。比如:
链接:
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
本文不对 Flask 与 FastAPI 的各个方面对进行对比,本人兴趣依然还是在 FastAPI 的 Swagger UI 功能,以及与 Flask 的 Blueprint 类似的特性。如果要拿 Flask 与 FastAPI 比较的话,应该用 Flask 2.x, 因为它开始支持类似 @app.get 的装饰器,并引入了 async 路由函数。
Flask 是在 2010 年发布的,它构建于 WSGI(Python Web Server Gateway Interface) 之上的,产品环境中运行需与 uWSGI, Gunicorn 搭配,或用 mod_wsgi 模块与 Apache 集成。因发布较早,所以目前应该有较多的使用者。Flask 2.0 需要 Python 3.6+ 的支持,如果支持
async, 需 Python 3.7+FastAPI 发布于 2018 年,构建于 ASGI(Asynchronous Server Gateway Interface) 之上,在 IO 密集型的应用中有更优越的性能。生成环境中配合 ASGI 服务器,如 Uvicorn 或 Hypercorn. FastAPI 最为亮丽的特性是集成了 Swagger UI -- 外加一个福利 ReDoc。FastAPI 需 Python 3.6+ 版本。
FastAPI 构建在 Starlette 之上, 更多类似包见 awesome-asgi。它的请求/响应数据定义,项目配置,校验库是 Pydantic, OpenAPI 自动生成的 Model 也靠它。Pydantic 使用了更高效的 JSON 处理器 ujson, Starlette 的 ORJSONResponse 使用了更利害的 orjson。
毕竟是在开始学一个新的框架,还是从它的基本用法开始,途中会穿插与 Flask 的对比。
FastAPI 的基本用法
安装:$ pip install fastapi当前安装的 fastapi 版本为 0.70.1, uvicorn 版本为 0.16.0; 也可以选择 hypercorn 来替代 uvicorn。开始第一个例子,摘自官方
$ pip install "uvicorn[standard]"
创建一个
main.py 文件,内容为 1from typing import Optional
2from fastapi import FastAPI
3
4app = FastAPI()
5
6
7@app.get("/")
8def read_root():
9 return {"Hello": "World"}
10
11
12@app.get("/items/{item_id}")
13def read_item(item_id: int, q: Optional[str] = None):
14 return {"item_id": item_id, "q": q} 注:以上两个函数前面可以加上
async 关键字来使用 coroutine。启动服务,用命令
$ uvicorn main:app --reload注:
INFO: Will watch for changes in these directories: ['/Users/yanbin/demo/first-fastapi']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [81730] using watchgod
INFO: Started server process [81732]
INFO: Waiting for application startup.
INFO: Application startup complete.
uvicorn --help 列出详细帮助,如启动多少个 worker, 绑定网络接口和端口号, 配置 SSL 证书等。访问服务
$ curl -i http://localhost:8000/另一个服务就是 http://localhost:8000/items/100?q=somequery
HTTP/1.1 200 OK
date: Mon, 20 Dec 2021 06:51:24 GMT
server: uvicorn
content-length: 17
content-type: application/json {"Hello":"World"}
与 Flask 的对比
- Flask 对返回的 dict 类型会自动应用 jsonify(), 并且响应的 Content-Type 为 application/json; 如果返回的是字符串,响应 Content-Type 为 text/html, 除非显式的调用 jsonify() 函数。FastAPI 的响应默认都会生成 application/json 的响应,Flask 对返回的 tuple 类型还有更多的约定, 见 Flask About Response
- Flask 对路径中的参数可选择在 @app.xxx 装饰器中声明类型,如
app.get("/items/<int:item_id>" - Flask 访问查询参数用
request.args.get('key')的方式 - 启动服务的方式不同,下面单独列出
回顾一个 Flask 启动服务的方式有
$ export FLASK_APP=hello如果程序文件名为
$ flask run
app.py 或 wsgi.py 的话,可以省略 FLASK_APP 环境变量,直接执行 flask run 就行。另一种方式就是在代码中加上 main 入口1if __name__ == "__main__":
2 app.run()然后像普通 Python 代码一样执行
$ python main.py对于 FastAPI, 如果总是要用
uvicorn 来启动服务的话,在 IDE 中调用就变得不那么便利。由于 uvicorn 本身就是 Python 实现,当然也就能把命令过程写到 Python 代码中1from fastapi import FastAPI
2import uvicorn
3
4app = FastAPI()
5
6if __name__ == "__main__":
7 uvicorn.run(app) 注:uvicorn.run() 可接受更多的参数,相当于
uvicorn --help 可接受的。同样把 main.py 当作普通 Python 程序来启动即可
$ python main.py
FastAPI 自动产生的 Web 文档
前面讲过,与 FastAPI 的结缘首要的原因就是它集成了 Swagger UI, 在启动了 FastAPI 服务后,只要访问 http://localhost:8000/docs, 熟悉的 Swagger UI 即刻映入眼帘
同时注意到函数映射为 API 的名称,由于参数是带有类型提示的,所以在 SwaggerUI 中也能看到相应的类型,Optional 的类型没有 *required 标识。其他定制 Swagger UI 的工作需要时可细细研究,如 1) 文档描述,2) API 名称定义, 3) API 描述, 4) API 验证 - Basic, OAuth, Keys如果是要从请求头里获取值,则在路由函数中定义如下参数:
1@app.get("/")
2def read_root(api_key:str = Header(None)):
3 return "something"这样在 SwaggerUI 和 Redoc 中也能看到该 API 需要从 HttpRequest Header 中获取
api-token 值,FastAPI 会用中杠替换下划线来作为 Header 的 Key.FastAPI 除了 SwaggerUI 外,还同时给了我们另一个选择,那就是 redoc, 访问 http://localhost:8000/redoc, 看到下面的界面
我也是头一回从 FastAPI 这里了解到还有一个 Redoc 的 Web API 文档工具,它和 SwaggerUI 类似,都是支持 Open API,但不能用 Redoc 来调用 API。API Spec 中包含请求/响应的 Model
使用 FastAPI 时,在 API 文档中产生请求/响应的 Model 类型也很简单,下面的例子同时包含自定义的输入输出类型下面是 main.py 的全新内容
1from typing import Optional
2from fastapi import FastAPI
3from pydantic import BaseModel
4
5app = FastAPI()
6
7class Item(BaseModel):
8 name: str
9 price: float
10 is_offer: Optional[bool] = None
11
12class Msg(BaseModel):
13 item_id: int
14 item_name: str
15
16@app.put("/items/{item_id}", response_model=Msg)
17def update_item(item_id: int, item: Item):
18 return {"item_name": item.name, "item_id": item_id} 再查看 http://localhost:8000/docs 文档,看到 PUT /items/{item_id} 的界面如下
我们能看到请求与响应中的 Example Value 和 Schema 定义梳理完 FastAPI 的自动 API 文档功能,进入下一个话题
FastAPI 的多文件支持
FastAPI 类似于 Flask 的 Blueprints 功能没有一个特定的名称,只笼统的给出解决方案 Bigger Applications - Multiple Files. 我们来看一下它是怎么实现的。这里简化官方的例子,排除了 dependencies 的应用,要用到的目录结构为1app
2├── internal
3│ └── admin.py
4├── main.py
5└── routers
6 ├── items.py
7 └── users.py每个目录中都可以放一个
__init__.py 文件,标识为包,接着是每个文件的内容app/internal/admin.py
1from fastapi import APIRouter
2
3router = APIRouter()
4
5@router.get("/")
6def admin():
7 return "admin"app/routers/items.py
1from fastapi import APIRouter
2
3router = APIRouter(prefix="/items", tags=["tag_items"])
4
5@router.get("/")
6async def read_items():
7 return {"__all__"}
8
9
10@router.delete("/{item_id}")
11def delete_item(item_id: int):
12 return {"action": "delete", "item_id": item_id}app/routers/users.py
1from fastapi import APIRouter
2
3router = APIRouter()
4
5@router.get("/users/{username}", tags=["users"])
6def read_user(username: str):
7 return {"username": username}
8
9@router.post("/users/{username}", tags=["users"])
10async def add_user(username: str):
11 return {"action": "add", "username": username, "userid": "new_id"}在每个单独定义路由的文件里用到的是
APIRouter, 它相当是一个迷你的 FastAPI, 也类似于 Flask 的 Blueprint. 上面分别演示了两种 APIRouter 的声明方式,声明时带不带 prefix 和 tags。同时在为函数定义路由时也可指定 tags,在后面我们将会注意到 tags 只是为 API 文档分类用的。app/main.py
1from fastapi import FastAPI
2from .internal import admin
3from .routers import users, items
4
5app = FastAPI()
6
7app.include_router(users.router)
8app.include_router(items.router)
9app.include_router(admin.router, prefix="/admin", tags=["admin"])
10
11@app.get("/")
12def index():
13 return "index"这里实际上使用了三种定义路径前缀与 tags(API 分类) 的方式
- user.router: 在每个 API 中定义完整 API 路径(如 /users 为 URL 前缀) 以及 tags
- item.router: 声明 APIRouter 时统一指定 URL 前缀和 tags
- admin.router: 注册 APIrouter 到 app(FastAPI) 时指定 URL 前缀和 tags
现在运行(如果之前运行 uvicorn 没有出现异常,由于有 --reload 参数会自动刷新)
$ uvicorn app.main:app --reload
查看 SwaggerUI http://localhost:8000/docs

FastAPI Sub Applications - Mounts
FastAPI 除 APIRouter 之外还为我们提供另一种方式来支持多模块,下面直接摘自官方 Sub Applications - Mounts 的代码 1from fastapi import FastAPI
2
3app = FastAPI()
4
5
6@app.get("/app")
7def read_main():
8 return {"message": "Hello World from main app"}
9
10
11subapi = FastAPI()
12
13
14@subapi.get("/sub")
15def read_sub():
16 return {"message": "Hello World from sub API"}
17
18
19app.mount("/subapi", subapi)大家都是 FastAPI, 用
app.mount("/subapi", subapi) 后 subapi 注解的 API 就是 app 的子项目,它们的 Endpoint 分别是- GET /app
- GET /subapi/sub
与 APIRouter 不同的是,它们有各自的 /docs 和 /redoc
- http://localhost:8000/docs 只能看到 /app
- http://localhost:8000/subapi/docs 只有 /subapi/sub
访问 /redoc, /subapi/docs 也是类似的效果。
这种方式针对不同客户端开发不同的 API, 或者前后台可以注册为不同的子项目,这让不同组的 API 文档能够相互独立。
到目前为止我重点关注 FastAPI 的就是以上那两个特性:自动 API 文档与大程序多文件(Blueprint) 功能。其他的特性可查阅 FastAPI 的 Features。比如:
- 自动的 JSON 反序列化
- 数据校验功能
- HTTP Basic, OAuth2, API keys 的支持
- WebSocket, 进程内后台任务,启动/关闭事件,CORS, GZIP, 静态文件,流响应数据,Session/Cookie
- 轻松与 GraphQL 集成
链接:
- Understanding Flask vs FastAPI Web Framework
- The Ultimate Face-off: Flask vs. FastAPI
- FastAPI vs Flask: Comparison Guid for Data Science Enthusiasts
- FastAPI vs Flask - The Complete Guide
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。