FastAPI - 快速指南


FastAPI - 简介

FastAPI是一个现代 Python Web 框架,在构建 API 方面非常高效。它基于自 Python 3.6 起添加的 Python 类型提示功能。它是 Python 最快的 Web 框架之一。

  • 由于它适用于 Starlette 和 Pydantic 库的功能,因此其性能是最好的,并且与 NodeJS 和 Go 的性能相当。

  • 除了提供高性能之外,FastAPI 还提供显着的开发速度、减少代码中人为引起的错误、易于学习并且完全可用于生产。

  • FastAPI 与众所周知的 API 标准完全兼容,即 OpenAPI 和 JSON 模式。

FastAPI 由Sebastian Ramirez于 2018 年 12 月开发。FastAPI 0.68.0 是当前可用的版本。

FastAPI – 环境设置

要安装 FastAPI(最好在虚拟环境中),请使用pip安装程序。

pip3 install fastapi

FastAPI 依赖于StarlettePydantic库,因此它们也会被安装。

使用 PIP 安装 Uvicorn

FastAPI 不附带任何内置服务器应用程序。要运行 FastAPI 应用程序,您需要一个名为uvicorn的 ASGI 服务器,因此也使用 pip 安装程序安装相同的服务器。它还将安装 uvicorn 的依赖项 - asgiref、click、h11 和 Typing-extensions

pip3 install uvicorn

安装这两个库后,我们可以检查到目前为止安装的所有库。

pip3 freeze
asgiref==3.4.1
click==8.0.1
colorama==0.4.4
fastapi==0.68.0
h11==0.12.0
importlib-metadata==4.6.4
pydantic==1.8.2
starlette==0.14.2
typing-extensions==3.10.0.0
uvicorn==0.15.0
zipp==3.5.0

FastAPI - 你好世界

入门

创建 FastAPI 应用程序的第一步是声明 FastAPI 类的应用程序对象。

from fastapi import FastAPI
app = FastAPI()

应用程序对象是应用程序与客户端浏览器交互的主要点。uvicorn服务器使用该对象来监听客户端的请求。

下一步是创建路径操作。路径是一个 URL,当客户端访问该 URL 时,它会调用访问映射到 HTTP 方法之一的 URL,并执行关联的函数。我们需要将视图函数绑定到 URL 和相应的 HTTP 方法。例如,index()函数对应于带有“get”操作的“/”路径。

@app.get("/")
async def root():
   return {"message": "Hello World"}

该函数返回 JSON 响应,但是,它可以返回dict、list、str、int等。它还可以返回 Pydantic 模型。

将以下代码保存为main.py

from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def index():
   return {"message": "Hello World"}

通过提及实例化 FastAPI 应用程序对象的文件来启动 uvicorn 服务器。

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.

打开浏览器并访问http://localhost:/8000。您将在浏览器窗口中看到 JSON 响应。

FastAPI 你好

FastAPI-开放API

在浏览器中输入以下 URL,即可自动生成交互式文档。

http://127.0.0.1:8000/docs

FastAPI 使用 Swagger UI 来生成此文档。浏览器将显示以下内容 -

FastAPI 开放API

单击“尝试”按钮,然后单击随后出现的“执行”按钮。

FastAPI OpenAPI1

您可以看到内部执行的Curl命令、请求 URL、响应标头以及服务器响应的 JSON 格式。

FastAPI 使用OpenAPI规范生成架构。规范决定了如何定义API路径、路径参数等。OpenAPI标准定义的API schema决定了如何使用JSON Schema发送数据。从浏览器访问 http://127.0.0.1:8000/openapi.json。将显示格式整齐的 JSON 响应,如下所示 -

{
   "openapi": "3.0.2",
   "info": {
      "title": "FastAPI",
      "version": "0.1.0"
   },
   "paths": {
      "/": {
         "get": {
            "summary": "Index",
            "operationId": "index__get",
            "responses": {
               "200": {
                  "description": "Successful Response",
                  "content": {
                     "application/json": {
                        "schema": {}
                     }
                  }
               }
            }
         }
      }
   }
}

FastAPI还支持Redoc提供的另一种自动文档方法(https://github.com/Redocly/redoc)。

在浏览器地址栏中输入 http://localhost:8000/redoc 作为 URL。

FastAPI GitHub

FastAPI - Uvicorn

与 Flask 框架不同,FastAPI 不包含任何内置开发服务器。因此我们需要Uvicorn。它执行ASGI标准并且速度快如闪电。ASGI 代表异步服务器网关接口

符合WSGI (Web 服务器网关接口 - 旧标准)Web 服务器不适合异步应用程序。实现 ASGI 规范的 Python Web 框架(例如 FastAPI)可提供高速性能,可与使用 Node 和 Go 构建的 Web 应用程序相媲美。

Uvicorn 使用uvloophttptools库。它还提供对 HTTP/2 和 WebSockets 的支持,这是 WSGI 无法处理的。uvloop id 类似于内置的asyncio事件循环。httptools库处理 http 协议。

如前所述,Uvicorn 的安装将以最少的依赖关系进行安装。但是,标准安装还将安装基于cython 的依赖项以及其他附加库。

pip3 install uvicorn(standard)

这样,WebSockets协议将得到支持。此外,将安装PyYAML以允许您提供 .yaml 文件。

如前所述,该应用程序是使用以下命令在 Uvicorn 服务器上启动的 -

uvicorn main:app –reload

--reload选项启用调试模式,以便app.py中任何更改都将自动反映,并且客户端浏览器上的显示将自动刷新。此外,可以使用以下命令行选项 -

先生编号 命令与说明
1

--主机文本

将套接字绑定到该主机。[默认127.0.0.1]

2

--端口整数

将套接字绑定到此端口。[默认8000]

3

--uds 文本

绑定到 UNIX 域套接字。

4

--fd 整数

从此文件描述符绑定到套接字。

5

--重新加载

启用自动重新加载。

6

--reload-dir 路径

显式设置重新加载目录,默认当前工作目录。

7

--reload-包含文本

观看时包含文件。默认情况下包含“*.py”

8

-重新加载-排除文本

监视文件时排除。

9

--重新加载延迟浮动

上一次检查和下一次检查之间的延迟默认 0.25

10

-loop [自动|异步|uvloop]

事件循环实现。[默认自动]

11

--http [自动|h11|httptools]

HTTP协议的实现。[默认自动]

12

--interface auto|asgi|asgi|wsgi

选择应用程序界面。[默认自动]

13

--env-文件路径

环境配置文件。

14

--log-config 路径

记录配置文件。支持的格式 .ini、.json、.yaml。

15

- 版本

显示 uvicorn 版本并退出。

16

--app-dir 文本

在指定目录默认当前目录下查找APP

17 号

- 帮助

显示此消息并退出。

Uvicorn 服务器也可以通过编程方式启动,而不是从命令行启动。

例子

在 Python 代码中,使用上面列出的任何参数调用uvicorn.run()方法 -

import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def index():
   return {"message": "Hello World"}
if __name__ == "__main__":
   uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)

现在将这个app.py作为 Python 脚本运行,如下所示 -

(fastapienv) C:\fastapienv>python app.py

Uvicorn 服务器因此将以调试模式启动。

FastAPI - 类型提示

FastAPI 广泛使用了Python 3.5 及以上版本中提供的类型提示功能。事实上,Python 被认为是一种动态类型语言。这也恰好是 Python 的独特功能。在Python代码中,变量不需要声明为属于某种类型,其类型由分配给它的瞬时值动态确定。Python 的解释器不执行类型检查,因此很容易出现运行时异常。

在以下示例中,division()函数定义有两个参数并返回它们的除法,假设参数为数字。

>>> def division(a, b):
   return a/b
>>> division(10, 4)
2.5
>>> division(10, 2.5)
4.0

但是,如果传递给函数的值之一恰好是非数字,则会导致 TypeError,如下所示 -

>>> division("Python",5)
TypeError: unsupported operand type(s) for /: 'str' and 'int'

即使是基本的编码环境(例如 IDLE)也表明该函数需要两个参数,但不会指定类型,因为它们尚未声明。

FastAPI 类型提示

Python 的新类型提示功能有助于提示用户要传递的参数的预期类型。这是通过在参数后添加冒号和数据类型来完成的。我们将重新定义 Division() 函数,如下所示 -

FastAPI 类型提示

请注意,在调用该函数时,Python 会提示要传递的每个参数的预期类型。但是,如果传递了不兼容的值,这并不能阻止出现 TypeError。在运行之前,您必须使用静态类型检查器(例如MyPy)来检查兼容性。

正如函数定义中的形式参数一样,可以为函数的返回值提供类型提示。在函数定义语句中的冒号符号之前(功能块在其后开始)添加一个箭头 (->) 和类型。

FastAPI 类型提示

但是,如前所述,如果将不兼容的值传递给函数或由函数返回,Python 会报告 TypeError。使用 MyPy 静态类型检查器可以检测此类错误。首先安装 mypy 包。

pip3 install mypy

将以下代码保存为 typecheck.py

def division(x:int, y:int) -> int:
   return (x//y)
a=division(10,2)
print (a)
b=division(5,2.5)
print (b)
c=division("Hello",10)
print (c)

使用 mypy 检查此代码是否存在类型错误。

C:\python37>mypy typechk.py
typechk.py:7: error: Argument 2 to "division" has incompatible
type "float"; expected "int"
typechk.py:10: error: Argument 1 to "division" has
incompatible type "str"; expected "int"
Found 2 errors in 1 file (checked 1 source file)

第二次和第三次调用该函数时出现错误。第二,当需要int时,传递给y 的值是float 。第三,当需要int时,传递给x 的值是str 。(请注意 // 运算符返回整数除法)

所有标准数据类型都可以用作类型提示。这可以通过全局变量、作为函数参数的变量、函数内部定义等来完成。

x: int = 3
y: float = 3.14
nm: str = 'abc'
married: bool = False
names: list = ['a', 'b', 'c']
marks: tuple = (10, 20, 30)
marklist: dict = {'a': 10, 'b': 20, 'c': 30}

较新版本的 Python(版本 3.5 及以上)标准库中新增的一个功能是输入模块。它为相应的标准集合类型定义了特殊类型。类型模块上的类型有List、Tuple、Dict 和 Sequence。它还由联合类型可选类型组成。请注意,数据类型的标准名称都是小写的,而打字模块中的数据类型的首字母是大写的。使用此功能,我们可以询问特定类型的集合。

from typing import List, Tuple, Dict
# following line declares a List object of strings.
# If violated, mypy shows error
cities: List[str] = ['Mumbai', 'Delhi', 'Chennai']
# This is Tuple with three elements respectively
# of str, int and float type)
employee: Tuple[str, int, float] = ('Ravi', 25, 35000)
# Similarly in the following Dict, the object key should be str
# and value should be of int type, failing which
# static type checker throws error
marklist: Dict[str, int] = {'Ravi': 61, 'Anil': 72}

FastAPI - IDE 支持

Python 的类型提示功能在几乎所有IDE(集成开发环境)(例如PyCharmVS Code )中最有效地使用,以提供动态自动完成功能。

让我们看看 VS Code 如何在编写代码时使用类型提示来提供自动完成建议。在下面的示例中,定义了一个名为sayhello且以 name 作为参数的函数。该函数通过在名称参数之间添加空格将“Hello”连接到名称参数来返回一个字符串。此外,还需要确保名称的第一个字母为大写。

Python 的str类有一个Capitalize()方法用于此目的,但如果在键入代码时不记得它,则必须在其他地方搜索它。如果在名称后面加一个点,则您期望看到属性列表,但不会显示任何内容,因为 Python 不知道名称变量的运行时类型是什么。

FastAPI IDE 支持

在这里,类型提示很方便。在函数定义中包含 str 作为名称类型。现在,当您在名称后面按点 (.) 时,会出现所有字符串方法的下拉列表,可以从中选择所需的方法(在本例中为 Capitalize())。

FastAPI IDE 支持

还可以将类型提示与用户定义的类一起使用。在以下示例中,使用__init__()构造函数参数的类型提示定义了一个矩形类。

class rectangle:
   def __init__(self, w:int, h:int) ->None:
      self.width=w
      self.height=h

以下是使用上述矩形类的对象作为参数的函数。声明中使用的类型提示是类的名称。

def area(r:rectangle)->int:
   return r.width*r.height
r1=rectangle(10,20)
print ("area = ", area(r1))

同样在这种情况下,IDE 编辑器提供自动完成支持,提示实例属性列表。以下是PyCharm编辑器的屏幕截图。

FastAPI IDE 支持

FastAPI广泛使用类型提示。此功能随处可见,例如路径参数、查询参数、标头、主体、依赖项等,以及验证传入请求中的数据。OpenAPI 文档生成也使用类型提示。

FastAPI - REST 架构

关系状态传输(REST) 是一种软件架构风格。REST 定义了 Web 应用程序架构的Behave方式。它是一种基于资源的架构,其中 REST 服务器托管的所有内容(文件、图像或数据库表中的行)都是资源,具有多种表示形式。

REST 建议一定的架构限制。

  • 统一接口

  • 无国籍状态

  • 客户端服务器

  • 可缓存性

  • 分层系统

  • 按需编码

REST 约束具有以下优点 -

  • 可扩展性

  • 简单

  • 可修改性

  • 可靠性

  • 可移植性

  • 能见度

REST 使用 HTTP 动词或方法对资源进行操作。POST、GET、PUT 和 DELETE 方法分别执行 CREATE、READ、UPDATE 和 DELETE 操作。

FastAPI - 路径参数

现代 Web 框架使用路由或端点作为 URL 的一部分,而不是基于文件的 URL。这有助于用户更有效地记住应用程序 URL。在 FastAPI 中,它被称为路径。路径或路由是 URL 中第一个“/”之后的部分。

例如,在以下 URL 中,

http://localhost:8000/hello/TutorialsPoint

路径或路线将是

/hello/TutorialsPoint

在FastAPI中,这样的路径字符串作为操作装饰器的参数给出。这里的操作指的是浏览器用来发送数据的HTTP动词。这些操作包括 GET、PUT 等。操作装饰器(例如,@app.get("/"))后面紧跟着一个函数,该函数在访问指定的 URL 时执行。在下面的例子中 -

from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def index():
   return {"message": "Hello World"}

这里,("/")是路径,get是操作,@app.get("/")是路径操作装饰器,其下面的index()函数称为路径操作函数。

以下任何 HTTP 动词都可以用作操作。

先生。 方法及说明
1

得到

以未加密的形式将数据发送到服务器。最常用的方法。

2

与 GET 相同,但没有响应正文。

3

邮政

用于将 HTML 表单数据发送到服务器。服务器不会缓存 POST 方法接收的数据。

4

将目标资源的所有当前表示替换为上传的内容。

5

删除

删除 URL 给出的目标资源的所有当前表示形式。

函数定义中的async关键字告诉 FastAPI 它将异步运行,即不会阻塞当前执行线程。但是,也可以在没有 async 前缀的情况下定义路径操作函数。

此修饰函数返回 JSON 响应。虽然它可以返回几乎任何Python的对象,但它会自动转换为JSON。在本教程中,我们将进一步了解此类函数如何返回Pydantic模型对象。

URL 的端点或路径可以具有一个或多个可变参数。可以使用 Python 的字符串格式化表示法来接受它们。在上面的示例 URL http://localhost:8000/hello/TutorialsPoint 中,最后一个值可能会在每个客户端请求中更改。该可变参数可以在路径中定义的变量中接受,并传递给绑定到操作装饰器的函数中定义的形式参数。

例子

在路由中添加另一个带有可变参数的路径装饰器,并绑定hello()函数以具有名称参数。按照以下内容修改 main.py。

import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def index():
   return {"message": "Hello World"}
@app.get("/hello/{name}")
async def hello(name):
   return {"name": name}

启动 Uvicorn 服务器并访问 http://localhost:8000/hello/Tutorialspoint URL。浏览器显示以下 JSON 响应。

{"name":"Tutorialspoint"}

将变量路径参数更改为其他内容,例如 http://localhost:8000/hello/Python,以便浏览器显示 -

{"name":"Python"}

检查 OpenAPI 文档

现在,如果我们通过输入 URL http://localhost:8000/docs 来检查 OpenAPI 文档,它将显示两条路由及其各自的视图函数。单击 /hello/{name} 按钮下方的“尝试”按钮,并将 Tutorialspoint 作为 name 参数描述的值,然后单击“执行”按钮。

FastAPI 路径参数

然后它将显示Curl命令、请求 URL以及服务器响应的详细信息(包括响应正文和响应标头)。

FastAPI 路径参数

一条路由可以有多个参数,用“/”符号分隔。

from fastapi import FastAPI
app = FastAPI()
@app.get("/hello/{name}/{age}")
async def hello(name,age):
   return {"name": name, "age":age}

在本例中,/hello是路由,后面跟着两个放在大括号中的参数。如果浏览器地址栏中给出的URL是http://localhost:8000/hello/Ravi/20,则Ravi和20的数据将分别分配给变量name和age。浏览器显示以下 JSON 响应 -

{"name":"Ravi","age":"20"}

带类型的路径参数

您可以使用Python的类型提示来修饰要修饰的函数的参数。在本例中,将 name 定义为 str,将age 定义为 int。

@app.get("/hello/{name}/{age}")
async def hello(name:str,age:int):
   return {"name": name, "age":age} 

如果类型不匹配,这将导致浏览器在 JSON 响应中显示 HTTP 错误消息。尝试输入 http://localhost:8000/hello/20/Ravi 作为 URL。浏览器的响应如下 -

{
   "detail": [
      {
         "loc": [
            "path",
            "age"
         ],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
      }
   ]
}

原因很明显,因为年龄是整数,不能接受字符串值。这也将反映在 Swagger UI (OpenAPI) 文档中。

FastAPI 路径参数

FastAPI - 查询参数

将请求数据传递到服务器的经典方法是将查询字符串附加到 URL。假设服务器上的 Python 脚本 (hello.py) 作为CGI执行,由与号 (&) 连接的键值对列表形成查询字符串,通过添加问号 (? ) 作为分隔符。例如 -

http://localhost/cgi-bin/hello.py?name=Ravi&age=20

URL 的尾部(?)之后是查询字符串,然后由服务器端脚本解析以进行进一步处理。

如前所述,查询字符串是由 & 符号连接的参数=值对的列表。FastAPI 自动将端点中非路径参数的部分视为查询字符串,并将其解析为参数及其值。这些参数被传递给操作装饰器下面的函数。

例子

from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
async def hello(name:str,age:int):
   return {"name": name, "age":age}

启动 Uvicorn 服务器并在浏览器中启动此 URL -

http://localhost:8000/hello?name=Ravi&age=20

您应该得到相同的 JSON 响应。但是,检查告诉您 FastAPI 已检测到 /hello 端点没有路径参数,但有查询参数。

FastAPI 查询参数

单击“尝试”按钮,输入“Ravi”和“20”作为值,然后按“执行”按钮。文档页面现在显示 Curl 命令、请求 URL 以及 HTTP 响应的正文和标头。

FastAPI 查询参数

例子

您可以使用Python的类型提示来修饰要修饰的函数的参数。在本例中,将 name 定义为 str,将age 定义为 int。

from fastapi import FastAPI
app = FastAPI()
@app.get("/hello/{name}")
async def hello(name:str,age:int):
   return {"name": name, "age":age}

尝试输入 http://localhost:8000/docs 作为 URL。这将打开 Swagger UI (OpenAPI) 文档。参数“name”是路径参数,“age”是查询参数

FastAPI 查询参数

FastAPI - 参数验证

可以对URL 的路径参数和查询参数应用验证条件。为了对路径参数应用验证条件,您需要导入 Path 类。除了参数的默认值之外,如果是字符串参数,您还可以指定最大和最小长度。

from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/hello/{name}")
async def hello(name:str=Path(...,min_length=3,
max_length=10)):
   return {"name": name}

如果浏览器的 URL 包含长度小于 3 或大于 10 的参数,如 (http://localhost:8000/hello/Tutorialspoint),则会出现相应的错误消息,例如 -

{
   "detail": [
      {
         "loc": [
            "path",
            "name"
         ],
         "msg": "ensure this value has at most 10 characters",
         "type": "value_error.any_str.max_length",
         "ctx": {
            "limit_value": 10
         }
      }
   ]
}

OpenAPI 文档还显示了应用的验证 -

FastAPI参数验证

验证规则也可以应用于数字参数,使用下面给出的运算符 -

  • gt - 大于

  • ge - 大于或等于

  • lt - 小于

  • le - 小于或等于

让我们修改上面的操作装饰器以包含年龄作为路径参数并应用验证。

from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/hello/{name}/{age}")
async def hello(*, name: str=Path(...,min_length=3 , max_length=10), age: int = Path(..., ge=1, le=100)):
   return {"name": name, "age":age}

在这种情况下,验证规则适用于参数名称和年龄。如果输入的 URL 是 http://localhost:8000/hello/hi/110,则 JSON 响应显示以下验证失败的解释 -

{
   "detail": [
      {
         "loc": [
            "path",
            "name"
         ],
         "msg": "ensure this value has at least 3 characters",
         "type": "value_error.any_str.min_length",
         "ctx": {
            "limit_value": 3
         }
      },
      {
         "loc": [
            "path",
            "age"
         ],
         "msg": "ensure this value is less than or equal to 100",
         "type": "value_error.number.not_le",
         "ctx": {
            "limit_value": 100
         }
      }
   ]
}

swagger UI 文档还标识了约束。

FastAPI参数验证

查询参数还可以应用验证规则。您必须将它们指定为 Query 类构造函数的参数的一部分。

让我们在上面的函数中添加一个名为Percent的查询参数,并将验证规则应用为ge=0(即大于等于 0)和lt=100(小于或等于 100)

from fastapi import FastAPI, Path, Query
@app.get("/hello/{name}/{age}")
async def hello(*, name: str=Path(...,min_length=3 ,
max_length=10), \
      age: int = Path(..., ge=1, le=100), \
      percent:float=Query(..., ge=0, le=100)):
   return {"name": name, "age":age}

如果输入的 URL 是 http://localhost:8000/hello/Ravi/20?percent=79,则浏览器显示以下 JSON 响应 -

{"name":"Ravi","age":20}

FastAPI 正确地将百分比识别为应用了验证条件的查询参数。它在 OpenAPI 文档中反映如下:

FastAPI参数验证

虽然客户端可以使用 GET 方法将路径和查询参数发送到 API 服务器,但我们需要应用 POST 方法来发送一些二进制数据作为 HTTP 请求的一部分。该二进制数据可以是任何 Python 类的对象的形式。它构成了请求主体。FastAPI 使用 Pydantic 库来实现此目的。

FastAPI - Pydantic

Pydantic是一个用于数据解析和验证的 Python 库。它使用较新版本的 Python(版本 3.6 及以上)的类型提示机制,并在运行时验证类型。Pydantic 定义了BaseModel类。它充当创建用户定义模型的基类。

以下代码将 Student 类定义为基于 BaseModel 的模型。

from typing import List
from pydantic import BaseModel
class Student(BaseModel):
   id: int
   name :str
   subjects: List[str] = []

Student类的属性是用类型提示声明的。请注意,主题属性是类型模块中定义的列表类型和内置列表类型。

我们可以用具有匹配结构的字典填充 Student 类的对象,如下所示 -

>>> data = {
   'id': 1,
   'name': 'Ravikumar',
   'subjects': ["Eng", "Maths", "Sci"],
}
>>> s1=Student(**data)
>>> print (s1)
id=1 name='Ravikumar' subjects=['Eng', 'Maths', 'Sci']
>>> s1
Student(id=1, name='Ravikumar', subjects=['Eng', 'Maths', 'Sci'])
>>> s1.dict()
{'id': 1, 'name': 'Ravikumar', 'subjects': ['Eng', 'Maths', 'Sci']}

Pydantic将尽可能自动转换数据类型。例如,即使字典中的 id 键被分配了数字的字符串表示形式(例如“123”),它也会将其强制转换为整数。但只要不可能,就会引发异常。

>>> data = {
   'id': [1,2],
   'name': 'Ravikumar',
   'subjects': ["Eng", "Maths", "Sci"],
}
>>> s1=Student(**data)
Traceback (most recent call last):
   File "<pyshell#13>", line 1, in <module>
      s1=Student(**data)
   File "pydantic\main.py", line 406, in
pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error
for Student
id
   value is not a valid integer (type=type_error.integer)

Pydantic 还包含一个 Field 类来声明模型属性的元数据和验证规则。首先修改 Student 类以在“name”属性上应用字段类型,如下所示 -

from typing import List
from pydantic import BaseModel, Field
class Student(BaseModel):
   id: int
   name :str = Field(None, title="The description of the item", max_length=10)
   subjects: List[str] = []

填充数据,如下所示。这里的name超出了规定的max_length。Pydantic按预期抛出ValidationError 。

>>> data = {
   'id': 1,
   'name': 'Ravikumar Sharma',
   'subjects': ["Eng", "Maths", "Sci"],
}
>>> s1=Student(**data)
Traceback (most recent call last):
   File "<pyshell#28>", line 1, in <module>
      s1=Student(**data)
   File "pydantic\main.py", line 406, in
pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Student
name
   ensure this value has at most 10 characters
   (type=value_error.any_str.max_length; limit_value=10)

Pydantic 模型可用于与SQLAlchemyPeewee等 ORM 模型进行映射。

FastAPI - 请求正文

我们现在将使用 Pydantic 模型对象作为客户端请求的请求主体。正如前面提到的,我们需要使用 POST 操作装饰器来达到目的。

import uvicorn
from fastapi import FastAPI
from typing import List
from pydantic import BaseModel, Field
app = FastAPI()
class Student(BaseModel):
   id: int
   name :str = Field(None, title="name of student", max_length=10)
   subjects: List[str] = []
@app.post("/students/")
async def student_data(s1: Student):
   return s1

可以看出,student_data()函数由@app.post()装饰器装饰,其 URL 端点为“/students/”。它从客户端的请求接收 Student 类的对象作为 Body 参数。要测试此路由,请启动 Uvicorn 服务器并通过访问 http://localhost:8000/docs 在浏览器中打开 Swagger UI 文档

文档指出“/students/”路由附加有带有POST方法的student_data()函数。在模式部分下,将列出学生模型。

FastAPI 请求正文

展开前面的节点以显示模型的结构

FastAPI 请求正文

单击尝试按钮以在请求正文中填写测试值。

FastAPI 请求正文

单击“执行”按钮并获取服务器的响应值。

FastAPI 请求正文

虽然 Pydantic 模型自动填充请求正文,但也可以使用奇异值向其添加属性。为此,我们需要使用Body类对象作为要修饰的操作函数的参数。

首先,我们需要从fastapi导入 Body 类。如以下示例所示,在@app.post()装饰器下面的Student_data()函数定义中将“name”和“marks”声明为 Body 参数。

import uvicorn
from fastapi import FastAPI, Body
@app.post("/students")
async def student_data(name:str=Body(...),
marks:int=Body(...)):
   return {"name":name,"marks": marks}

如果我们检查 Swagger UI 文档,我们应该能够找到与 Student_data() 函数关联的这个 POST 方法,并且有一个带有两个参数的请求正文。

FastAPI 请求正文

还可以声明一个操作函数,使其具有路径和/或查询参数以及请求正文。让我们修改 Student_data() 函数,将路径参数“college”、“age”作为查询参数,将 Student 模型对象作为主体参数。

@app.post("/students/{college}")
async def student_data(college:str, age:int, student:Student):
   retval={"college":college, "age":age, **student.dict()}
   return retval

该函数将大学和年龄参数的值与 Student 对象的字典表示形式相加,并将其作为响应返回。我们可以检查 API 文档如下 -

FastAPI 请求正文

可以看出,college是路径参数,age是查询参数,Student模型是请求体。

FastAPI - 模板

默认情况下,FastAPI 向客户端呈现 JSON 响应。但是,它可以转换为 HTML 响应。为此,FastAPI在fastapi.responses模块中定义了HTMLResponse类。我们需要将response_class作为附加参数添加到操作装饰器中,并以HTMLResponse对象作为其值。

在以下示例中,@app.get() 装饰器具有“/hello/”端点和作为response_class 的HTMLResponse。在 hello() 函数内,我们有一个 Hello World 消息的 HTML 代码的字符串表示形式。该字符串以 HTML 响应的形式返回。

from fastapi.responses import HTMLResponse
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello/")
async def hello():
   ret='''
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
'''
   return HTMLResponse(content=ret)

通过检查 API 文档,可以看出服务器的响应正文是 HTML 格式的。

FastAPI 模板

请求 URL (http://localhost:8000/hello/) 也应该在浏览器中呈现消息。然而,呈现原始 HTML 响应非常乏味。或者,可以将预构建的 HTML 页面呈现为模板。为此,我们需要使用网页模板库。

Web 模板库有一个模板引擎,可以合并具有占位符变量的静态网页。来自任何来源(例如数据库)的数据被合并以动态生成和呈现网页。FastAPI没有任何预先打包的模板库。因此,人们可以自由地使用任何适合他需要的一种。在本教程中,我们将使用jinja2,一个非常流行的 Web 模板库。让我们首先使用 pip 安装程序安装它。

pip3 install jinja2

FastAPI 对 Jinja 模板的支持以 fastapi.templates 模块中定义的jinja2Templates类的形式提供。

from fastapi.templating import Jinja2Templates

要声明模板对象,应将存储 html 模板的文件夹作为参数提供。在当前工作目录中,我们将创建一个“templates”目录。

templates = Jinja2Templates(directory="templates")

用于呈现 Hello World 消息的简单网页“hello.html”也放置在“templates”文件夹中。

<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

我们现在要将此页面的 html 代码呈现为 HTMLResponse。让我们修改 hello() 函数如下 -

from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi import FastAPI, Request
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/hello/", response_class=HTMLResponse)
async def hello(request: Request):
   return templates.TemplateResponse("hello.html", {"request": request})

这里,模板对象的templateResponse()方法收集模板代码和请求上下文来呈现 http 响应。当我们启动服务器并访问 http://localhost:8000/hello/ URL 时,我们会在浏览器中看到Hello World消息,这实际上是hello.html的输出

FastAPI 模板

如前所述,jinja2 模板允许将某些占位符嵌入 HTML 代码中。jinja2 代码元素放在大括号内。一旦浏览器的 HTML 解析器遇到这种情况,模板引擎就会接管并通过 HTTP 响应提供的变量数据填充这些代码元素。Jinja2 提供以下代码元素 -

  • {% %} – 语句

  • {{ }} – 要打印到模板输出的表达式

  • {# #} - 不包含在模板输出中的注释

  • # # # - 行语句

hello.html修改如下,通过替换 name 参数来显示动态消息

<html>
<body>
<h2>Hello {{name}} Welcome to FastAPI</h2>
</body>
</html>

操作函数hello()也被修改为接受名称作为路径参数。TemplateResponse还应包含“name”的 JSON 表示形式name以及请求上下文。

from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi import FastAPI, Request
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/hello/{name}", response_class=HTMLResponse)
async def hello(request: Request, name:str):
   return templates.TemplateResponse("hello.html", {"request": request, "name":name})

重新启动服务器并访问 http://localhost:8000/hello/Kiran。浏览器现在使用此 URL 中的路径参数填充 jinja2 占位符。

FastAPI 模板

FastAPI - 静态文件

通常需要在模板响应中包含一些即使存在某些动态数据也保持不变的资源。此类资源称为静态资产。媒体文件(.png、.jpg 等)、用于执行某些前端代码的 JavaScript 文件或用于格式化 HTML(.CSS 文件)的样式表都是静态文件的示例。

为了处理静态文件,您需要一个名为aiofiles的库

pip3 install aiofiles

接下来,从fastapi.staticfiles模块导入StaticFiles类。它的对象是 FastAPI 应用程序对象的mount()方法的参数之一,用于在当前应用程序文件夹中分配“static”子文件夹来存储和提供应用程序的所有静态资源。

app.mount(app.mount("/static", StaticFiles(directory="static"), name="static")

例子

在以下示例中,FastAPI 徽标将在 hello.html 模板中呈现。因此,“fa-logo.png”文件首先放置在静态文件夹中。现在可以在 HTML 代码中用作<img>标记的src属性。

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
app = FastAPI()
templates = Jinja2Templates(directory="templates")
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/hello/{name}", response_class=HTMLResponse)
async def hello(request: Request, name:str):
   return templates.TemplateResponse("hello.html", {"request": request, "name":name})

\templates\hello.html的 HTML 代码如下 -

<html>
   <body>
      <h2>Hello {{name}} Welcome to FastAPI</h2>
      <img src="{{ url_for('static', path='fa-logo.png') }}" alt="" width="300">
   </body>
</html>
</pre>

运行 Uvicorn 服务器并访问 URL:http://localhost/hello/Vijay。徽标出现在浏览器窗口中,如图所示。

FastAPI 静态文件

例子

这是静态文件的另一个示例。JavaScript 代码 hello.js 包含要在以下 HTML 脚本 (\templates\hello.html) 中的onload事件上执行的myfunction()的定义

<html>
   <head>
      <title>My Website</title>
      <script src="{{ url_for('static', path='hello.js') }}"></script>
   </head>
   <body onload="myFunction()">
      <div id="time" style="text-align:right; width="100%"></div>
      <h1><div id="ttl">{{ name }}</div></h1>
   </body>
</html>

hello.js代码如下 - ( \static\hello.js)

function myFunction() {
   var today = new Date();
   var h = today.getHours();
   var m = today.getMinutes();
   var s = today.getSeconds();
   var msg="";
   if (h<12) {
      msg="Good Morning, ";
   }
   if (h>=12 && h<18) {
      msg="Good Afternoon, ";
   }
   if (h>=18) {
      msg="Good Evening, ";
   }
   var x=document.getElementById('ttl').innerHTML;
   document.getElementById('ttl').innerHTML = msg+x;
   document.getElementById('time').innerHTML = h + ":" + m + ":" + s;
}

该函数检测当前时间的值,并根据一天中的时间为msg变量分配适当的值(早上好、下午好或晚上好)。

保存/static/hello.js,修改\templates\hello.html并重新启动服务器。浏览器应显示当前时间及其下方相应的消息。

FastAPI 静态文件 FastAPI 静态文件

FastAPI - HTML 表单模板

让我们向应用程序添加另一个路由“/login”,它呈现一个具有简单登录表单的 html 模板。登录页面的 HTML 代码如下 -

<html>
   <body>
      <form action="/submit" method="POST">
         <h3>Enter User name</h3>
         <p><input type='text' name='nm'/></p>
         <h3>Enter Password</h3>
         <p><input type='password' name='pwd'/></p>
         <p><input type='submit' value='Login'/></p>
      </form>
   </body>
</html>

请注意,操作参数设置为“/submit”路由,操作设置为 POST。这对于进一步讨论具有重要意义。

在main.py文件中添加login()函数,如下所示 -

@app.get("/login/", response_class=HTMLResponse)
async def login(request: Request):
   return templates.TemplateResponse("login.html", {"request": request})

URL http://localhost:8000/login 将呈现登录表单,如下所示 -

FastAPI HTML 表单模板

FastAPI - 访问表单数据

现在我们将看到如何在FastAPI操作函数中访问HTML表单数据。在上面的示例中,/login 路由呈现一个登录表单。用户输入的数据以POST为请求方式提交到/submit URL。现在我们要提供一个视图函数来处理用户提交的数据。

FastAPI 有一个 Form 类,用于处理通过提交 HTML 表单作为请求接收到的数据。但是,您需要安装python-multipart模块。它是 Python 的流式多部分表单解析器。

pip3 install python-multipart

Form类添加到从 FastAPI 导入的资源中

from fastapi import Form

让我们定义一个用@app.post()修饰的submit()函数。为了接收表单数据,需要声明两个 Form 类型的参数,其名称与表单属性相同。

@app.post("/submit/")
async def submit(nm: str = Form(...), pwd: str = Form(...)):
   return {"username": nm}

填写文本字段后按提交。浏览器被重定向到 /submit URL 并呈现 JSON 响应。检查/submit路由的 Swagger API 文档。它正确地将nmpwd识别为请求正文参数,并将表单的“媒体类型”识别为application/x-www-form-urlencoded。

FastAPI 访问表单数据

甚至可以使用 HTML 表单数据填充并返回 Pydantic 模型。在下面的代码中,我们将 User 类声明为 Pydantic 模型,并将其对象作为服务器的响应发送。

from pydantic import BaseModel
class User(BaseModel):
   username:str
   password:str
@app.post("/submit/", response_model=User)
async def submit(nm: str = Form(...), pwd: str = Form(...)):
   return User(username=nm, password=pwd)

FastAPI - 上传文件

首先,要将文件发送到服务器,您需要使用 HTML 表单的 enctype 作为multipart/form-data,并使用输入类型作为文件来呈现一个按钮,单击该按钮时您可以从文件系统。

<html>
   <body>
      <form action="http://localhost:8000/uploader" method="POST" enctype="multipart/form-data">
         <input type="file" name="file" />
         <input type="submit"/>
      </form>
   </body>
</html>

请注意,表单的操作参数为端点 http://localhost:8000/uploader,方法设置为 POST。

此 HTML 表单使用以下代码呈现为模板 -

from fastapi import FastAPI, File, UploadFile, Request
import uvicorn
import shutil
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/upload/", response_class=HTMLResponse)
async def upload(request: Request):
   return templates.TemplateResponse("uploadfile.html", {"request": request})

访问http://localhost:8000/upload/。您应该通过“选择文件”按钮获取表格。单击它可以打开要上传的文件。

FastAPI上传文件

上传操作由FastAPI中的UploadFile函数处理

from fastapi import FastAPI, File, UploadFile
import shutil
@app.post("/uploader/")
async def create_upload_file(file: UploadFile = File(...)):
   with open("destination.png", "wb") as buffer:
      shutil.copyfileobj(file.file, buffer)
   return {"filename": file.filename}

我们将使用Python中的shutil库将接收到的文件复制到服务器位置,名称为destination.png

FastAPI - Cookie 参数

cookie是 HTTP 标头之一Web 服务器向客户端发送响应,除了请求的数据外,还插入一个或多个 cookie。cookie是存储在客户端计算机中非常少量的数据。在来自同一客户端的后续连接请求中,此 cookie 数据也会与 HTTP 请求一起附加。

cookie 对于记录有关客户端浏览的信息很有用。Cookie 是通过 HTTP 协议在无状态通信中检索状态信息的可靠方法。

在 FastAPI 中,cookie 参数是在set_cookie()方法的帮助下在响应对象上设置的

response.set_cookie(key, value)

例子

这是set_cookie()方法的示例。我们有一个名为 content 的 JSON 响应对象。调用set_cookie()方法将 cookie 设置为key="usrname"value="admin" -

from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/cookie/")
def create_cookie():
   content = {"message": "cookie set"}
   response = JSONResponse(content=content)
   response.set_cookie(key="username", value="admin")
   return response

要在后续访问时读回 cookie,请使用 FastAPI 库中的 Cookie 对象。

from fastapi import FastAPI, Cookie
app = FastAPI()
@app.get("/readcookie/")
async def read_cookie(username: str = Cookie(None)):
   return {"username": username}

检查 Swagger API 中的这两个端点。有这两个路由“/cookies”“/readcookie”。执行绑定到“/cookies”的create_cookie()函数。尽管设置了 cookie,但响应只是内容。

FastAPI Cookie 参数

当执行read_cookie()函数时,cookie 被读回并显示为响应。另外,文档也没有将用户名标识为 cookie 参数。

FastAPI Cookie 参数

FastAPI - 标头参数

为了读取作为客户端请求一部分的HTTP 标头的值,请从 FastAPI 库导入 Header 对象,并在操作函数定义中声明 Header 类型的参数。参数名称应与camel_case中转换的HTTP标头匹配。

在以下示例中,将检索“accept-language”标头。由于Python不允许在标识符名称中使用“-”(破折号),因此将其替换为“_”(下划线)

from typing import Optional
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/headers/")
async def read_header(accept_language: Optional[str] = Header(None)):
   return {"Accept-Language": accept_language} 

如以下 Swagger 文档所示,检索到的标头显示为响应正文。

FastAPI 标头参数

您可以在响应对象中推送自定义标头和预定义标头。操作函数应该有一个Response类型的参数。为了设置自定义标头,其名称应以“X”为前缀。在以下情况下,将添加一个名为“X-Web-Framework”的自定义标头和一个预定义标头“Content-Language”以及操作函数的响应。

from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/rspheader/")
def set_rsp_headers():
   content = {"message": "Hello World"}
   headers = {"X-Web-Framework": "FastAPI", "Content-Language": "en-US"}
   return JSONResponse(content=content, headers=headers)

新添加的标头将出现在文档的响应标头部分中。

FastAPI 标头参数

FastAPI - 响应模型

操作函数向客户端返回 JSON 响应。响应可以是Python主要类型的形式,即数字、字符串、列表或字典等。它也可以是Pydantic模型的形式。对于返回模型对象的函数,操作装饰器应声明一个respone_model参数。

在response_model的帮助下,FastAPI将输出数据转换为模型类的结构。它在 OpenAPI 路径操作中验证数据,为响应添加 JSON 架构。

response_model 参数的重要优点之一是我们可以通过从模型中选择字段来格式化输出,以将响应转换为输出模型。

例子

在下面的示例中,POST 操作装饰器以学生类(BaseModel 的子类)的对象的形式接收请求正文。由于该类中的字段之一,即响应中不需要标记(标记列表),我们定义了另一个称为percent的模型并将其用作response_model参数。

from typing import List
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class student(BaseModel):
   id: int
   name :str = Field(None, title="name of student", max_length=10)
   marks: List[int] = []
   percent_marks: float
class percent(BaseModel):
   id:int
   name :str = Field(None, title="name of student", max_length=10)
   percent_marks: float
@app.post("/marks", response_model=percent)
async def get_percent(s1:student):
   s1.percent_marks=sum(s1.marks)/2
   return s1

如果我们检查 Swagger 文档,它显示“/marks”路由获取学生班级的对象作为请求正文。使用适当的值填充属性并执行get_percent()函数。

FastAPI响应模型

服务器响应被转换为百分比类,因为它已用作response_model。

FastAPI响应模型

FastAPI - 嵌套模型

Pydantic模型的每个属性都有一个类型。该类型可以是内置的 Python 类型或模型本身。因此,可以声明具有特定属性名称、类型和验证的嵌套 JSON“对象”。

例子

在下面的示例中,我们构建一个客户模型,其中一个属性作为产品模型类。产品模型又具有供应商类别的属性。

from typing import Tuple
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class supplier(BaseModel):
   supplierID:int
   supplierName:str
class product(BaseModel):
   productID:int
   prodname:str
   price:int
   supp:supplier
class customer(BaseModel):
   custID:int
   custname:str
   prod:Tuple[product]

以下 POST 操作装饰器将客户模型的对象呈现为服务器响应。

@app.post('/invoice')
async def getInvoice(c1:customer):
   return c1

swagger UI 页面显示了三个模式的存在,对应于三个 BaseModel 类。

FastAPI 嵌套模型

展开以显示所有节点时的客户模式如下所示 -

FastAPI 嵌套模型

“/invoice”路由的示例响应应如下所示 -

{
   "custID": 1,
   "custname": "Jay",
   "prod": [
      {
         "productID": 1,
         "prodname": "LAPTOP",
         "price": 40000,
         "supp": {
            "supplierID": 1,
            "supplierName": "Dell"
         }
      }
   ]
}

FastAPI - 依赖关系

FastAPI 内置的依赖注入系统使您在构建 API 时可以更轻松地集成组件。在编程中,依赖注入是指一个对象接收它所依赖的其他对象的机制。其他对象称为依赖项。依赖注入具有以下优点 -

  • 重用相同的共享逻辑

  • 共享数据库连接

  • 强制执行身份验证和安全功能

假设FastAPI应用程序有两个操作函数,两者都有相同的查询参数id、name和age。

from fastapi import FastAPI
app = FastAPI()
@app.get("/user/")
async def user(id: str, name: str, age: int):
   return {"id": id, "name": name, "age": age}
@app.get("/admin/")
async def admin(id: str, name: str, age: int):
   return {"id": id, "name": name, "age": age}

如果发生任何更改(例如添加/删除查询参数),则需要更改路由装饰器和函数。

FastAPI 提供了Depends类,在这种情况下,它的对象被用作公共参数。首先从 FastAPI 导入Depends并定义一个函数来接收这些参数 -

async def dependency(id: str, name: str, age: int):
   return {"id": id, "name": name, "age": age}

现在我们可以使用该函数的返回值作为操作函数的参数

@app.get("/user/")
async def user(dep: dict = Depends(dependency)):
   return dep

对于每个新的请求,FastAPI 使用相应的参数调用依赖函数,返回结果,并将结果分配给您的操作。

您可以使用类而不是函数来管理依赖项。声明一个以 id、name 和age 作为属性的类。

class dependency:
   def __init__(self, id: str, name: str, age: int):
      self.id = id
      self.name = name
      self.age = age 

使用此类作为参数的类型。

@app.get("/user/")
async def user(dep: dependency = Depends(dependency)):