FastAPI
FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 并基于标准的 Python 类型提示。
关键特性:
- 快速:可与 NodeJS 和 Go 并肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。
- 高效编码:提高功能开发速度约 200% 至 300%。*
- 更少 bug:减少约 40% 的人为(开发者)导致错误。*
- 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
- 简单:设计的易于使用和学习,阅读文档的时间更短。
- 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。
- 健壮:生产可用级别的代码。还有自动生成的交互式文档。
- 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。
依赖¶
Python 及更高版本
FastAPI 站在以下巨人的肩膀之上:
安装¶
shell代码解读复制代码pip install fastapi
你还会需要一个 ASGI 服务器,生产环境可以使用 Uvicorn 或者 Hypercorn。
shell代码解读复制代码pip install "uvicorn[standard]"
示例¶
创建¶
- 创建一个
main.py
文件并写入以下内容:
python 代码解读复制代码from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
或者使用 async def
...
如果你的代码里会出现 async
/ await
,请使用 async def
:
python 代码解读复制代码from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
Note:
如果你不知道是否会用到,可以查看文档的 "In a hurry?" 章节中 关于 async
和 await
的部分。
运行¶
通过以下命令运行服务器:
vbnet 代码解读复制代码uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
关于 uvicorn main:app --reload
命令......
uvicorn main:app
命令含义如下:
main
:main.py
文件(一个 Python "模块")。app
:在main.py
文件中通过app = FastAPI()
创建的对象。--reload
:让服务器在更新代码后重新启动。仅在开发时使用该选项。
检查¶
使用浏览器访问 http://127.0.0.1:8000/items/5?q=somequery。
你将会看到如下 JSON 响应:
json 代码解读复制代码{"item_id": 5, "q": "somequery"}
你已经创建了一个具有以下功能的 API:
- 通过 路径
/
和/items/{item_id}
接受 HTTP 请求。 - 以上 路径 都接受
GET
操作(也被称为 HTTP 方法)。 /items/{item_id}
路径 有一个 路径参数item_id
并且应该为int
类型。/items/{item_id}
路径 有一个可选的str
类型的 查询参数q
。
交互式 API 文档¶
现在访问 http://127.0.0.1:8000/docs。
你会看到自动生成的交互式 API 文档(由 Swagger UI生成):
可选的 API 文档¶
访问 http://127.0.0.1:8000/redoc。
你会看到另一个自动生成的文档(由 ReDoc 生成):
示例升级¶
现在修改 main.py
文件来从 PUT
请求中接收请求体。
我们借助 Pydantic 来使用标准的 Python 类型声明请求体。
python 代码解读复制代码from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
服务器将会自动重载(因为在上面的步骤中你向 uvicorn
命令添加了 --reload
选项)。
交互式 API 文档升级¶
访问 http://127.0.0.1:8000/docs。
- 交互式 API 文档将会自动更新,并加入新的请求体:
- 点击「Try it out」按钮,之后你可以填写参数并直接调用 API:
- 然后点击「Execute」按钮,用户界面将会和 API 进行通信,发送参数,获取结果并在屏幕上展示:
可选文档升级¶
访问 http://127.0.0.1:8000/redoc。
- 可选文档同样会体现新加入的请求参数和请求体:
总结¶
总的来说,你就像声明函数的参数类型一样只声明了一次请求参数、请求体等的类型。
你使用了标准的现代 Python 类型来完成声明。
你不需要去学习新的语法、了解特定库的方法或类,等等。
只需要使用标准的 Python 及更高版本。
举个例子,比如声明 int
类型:
arduino 代码解读复制代码item_id: int
或者一个更复杂的 Item
模型:
makefile 代码解读复制代码item: Item
......在进行一次声明之后,你将获得:
-
编辑器支持,包括:
- 自动补全
- 类型检查
-
数据校验:
- 在校验失败时自动生成清晰的错误信息
- 对多层嵌套的 JSON 对象依然执行校验
-
转换 来自网络请求的输入数据为 Python 数据类型。包括以下数据:
- JSON
- 路径参数
- 查询参数
- Cookies
- 请求头
- 表单
- 文件
-
转换 输出的数据:转换 Python 数据类型为供网络传输的 JSON 数据:
- 转换 Python 基础类型 (
str
、int
、float
、bool
、list
等) datetime
对象UUID
对象- 数据库模型
- ......以及更多其他类型
- 转换 Python 基础类型 (
-
自动生成的交互式 API 文档,包括两种可选的用户界面:
- Swagger UI
- ReDoc
回到前面的代码示例,FastAPI 将会:
-
校验
GET
和PUT
请求的路径中是否含有item_id
。 -
校验
GET
和PUT
请求中的item_id
是否为int
类型。- 如果不是,客户端将会收到清晰有用的错误信息。
-
检查
GET
请求中是否有命名为q
的可选查询参数(比如http://127.0.0.1:8000/items/foo?q=somequery
)。- 因为
q
被声明为= None
,所以它是可选的。 - 如果没有
None
它将会是必需的 (如PUT
例子中的请求体)。
- 因为
-
对于访问
/items/{item_id}
的PUT
请求,将请求体读取为 JSON 并:- 检查是否有必需属性
name
并且值为str
类型 。 - 检查是否有必需属性
price
并且值为float
类型。 - 检查是否有可选属性
is_offer
, 如果有的话值应该为bool
类型。 - 以上过程对于多层嵌套的 JSON 对象同样也会执行
- 检查是否有必需属性
-
自动对 JSON 进行转换或转换成 JSON。
-
通过 OpenAPI 文档来记录所有内容,可被用于:
- 交互式文档系统
- 许多编程语言的客户端代码自动生成系统
-
直接提供 2 种交互式文档 web 界面。
虽然我们才刚刚开始,但其实你已经了解了这一切是如何工作的。
尝试更改下面这行代码:
kotlin 代码解读复制代码 return {"item_name": item.name, "item_id": item_id}
......从:
erlang 代码解读复制代码 ... "item_name": item.name ...
......改为:
erlang 代码解读复制代码 ... "item_price": item.price ...
......注意观察编辑器是如何自动补全属性并且还知道它们的类型:
Python 类型提示简介¶
Python 3.6+ 版本加入了对"类型提示"的支持。
这些 "类型提示" 是一种新的语法(在 Python 3.6 版本加入)用来声明一个变量的类型。
通过声明变量的类型,编辑器和一些工具能给你提供更好的支持。
这只是一个关于 Python 类型提示的快速入门 / 复习。它仅涵盖与 FastAPI 一起使用所需的最少部分...实际上只有很少一点。
整个 FastAPI 都基于这些类型提示构建,它们带来了许多优点和好处。
但即使你不会用到 FastAPI,了解一下类型提示也会让你从中受益。
动机¶
让我们从一个简单的例子开始:
scss 代码解读复制代码def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
运行这段程序将输出:
代码解读复制代码John Doe
这个函数做了下面这些事情:
- 接收
first_name
和last_name
参数。 - 通过
title()
将每个参数的第一个字母转换为大写形式。 - 中间用一个空格来拼接它们。
diff 代码解读复制代码def get_full_name(first_name, last_name):
+ full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
修改示例¶
这是一个非常简单的程序。
现在假设你将从头开始编写这段程序。
在某一时刻,你开始定义函数,并且准备好了参数...。
现在你需要调用一个"将第一个字母转换为大写形式的方法"。
等等,那个方法是什么来着?upper
?还是 uppercase
?first_uppercase
?capitalize
?
然后你尝试向程序员老手的朋友——编辑器自动补全寻求帮助。
输入函数的第一个参数 first_name
,输入点号(.
)然后敲下 Ctrl+Space
来触发代码补全。
但遗憾的是并没有起什么作用:
添加类型¶
让我们来修改上面例子的一行代码。
我们将把下面这段代码中的函数参数从:
markdown 代码解读复制代码 first_name, last_name
改成:
python 代码解读复制代码 first_name: str, last_name: str
就是这样。
这些就是"类型提示":
diff 代码解读复制代码+ def get_full_name(first_name: str, last_name: str):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
这和声明默认值是不同的,例如:
ini 代码解读复制代码 first_name="john", last_name="doe"
这两者不一样。
我们用的是冒号(:
),不是等号(=
)。
而且添加类型提示一般不会改变原来的运行结果。
现在假设我们又一次正在创建这个函数,这次添加了类型提示。
在同样的地方,通过 Ctrl+Space
触发自动补全,你会发现:
这样,你可以滚动查看选项,直到你找到看起来眼熟的那个:
更多动机¶
下面是一个已经有类型提示的函数:
diff 代码解读复制代码+ def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + age
return name_with_age
因为编辑器已经知道了这些变量的类型,所以不仅能对代码进行补全,还能检查其中的错误:
现在你知道了必须先修复这个问题,通过 str(age)
把 age
转换成字符串:
diff 代码解读复制代码def get_name_with_age(name: str, age: int):
+ name_with_age = name + " is this old: " + str(age)
return name_with_age
Starlette介绍
Starlette 是一个轻量级的 ASGI 框架/工具包, 非常适合在 Python 中构建异步 Web 服务。
它已准备好用于生产,并为您提供以下功能:
- 一个轻量级、低复杂度的 HTTP Web 框架。
- WebSocket 支持。
- 进程内后台任务。
- 启动和关闭事件。
- 基于 构建的测试客户端。
httpx
- CORS、GZip、静态文件、流式响应。
- 会话和 Cookie 支持。
- 100% 的测试覆盖率。
- 100% 类型注释的代码库。
- 很少的硬依赖项。
- 兼容 和 后端。
asyncio``trio
- 与独立基准测试相比,整体性能出色。
安装
代码解读复制代码pip install starlette
您还需要安装 ASGI 服务器,例如 uvicorn、daphne 或 hypercorn。
代码解读复制代码pip install uvicorn
例
main.py
python 代码解读复制代码from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def homepage(request):
return JSONResponse({'hello': 'world'})
app = Starlette(debug=True, routes=[
Route('/', homepage),
])
然后运行应用程序...
css 代码解读复制代码uvicorn main:app
评论记录:
回复评论: