Fastapi 简明教程

FastAPI - Quick Guide

FastAPI - Introduction

FastAPI 是一款现代的 Python Web 框架,非常适合用来构建 API。它基于 Python 的类型提示功能,该功能已从 Python 3.6 起添加。它是 Python 中最快的 Web 框架之一。

  1. 由于它基于 Starlette 和 Pydantic 库的功能,因此其性能名列前茅,并且与 NodeJS 和 Go 不相上下。

  2. FastAPI 除了提供高性能外,还提供极快的开发速度,减少代码中人为导致的错误,易于学习,并且完全可以投入生产。

  3. FastAPI 与著名的 API 标准(即 OpenAPI 和 JSON 模式)完全兼容。

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

FastAPI – EnvironmentSetup

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

pip3 install fastapi

FastAPI 依赖 StarlettePydantic 库,因此它们也会一同安装。

Installing Uvicorn using PIP

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 - Hello World

Getting Started

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

from fastapi import FastAPI
app = FastAPI()

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

下一步是创建路径操作。路径是一个 URL,当客户端访问此 URL 时,该 URL 访问映射到 HTTP 方法之一,要执行一个关联的函数。我们需要将视图函数绑定到 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.

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

hello

FastAPI - OpenAPI

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

http://127.0.0.1:8000/docs

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

openapi

单击 'try it out' 按钮,然后单击随后出现的 'Execute' 按钮。

openapi1

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

FastAPI 使用 OpenAPI 规范生成模式。该规范确定如何定义 API 路径、路径参数等。由 OpenAPI 标准定义的 API 架构决定如何使用 JSON 架构发送数据。从浏览器访问 [role="bare"] [role="bare"]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 )提供的另一种自动文档方法。

将 [role="bare"] [role="bare"]http://localhost:8000/redoc 输入浏览器地址栏作为 URL。

github

FastAPI - Uvicorn

与 Flask 框架不同,FastAPI 不包含任何内置开发服务器。因此我们需要 Uvicorn 。它实现了 ASGI 标准且非常快。ASGI 代表 Asynchronous Server Gateway Interface

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

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

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

pip3 install uvicorn(standard)

使用此方法将会支持 WebSockets 协议。另外,将会安装 PyYAML ,以便您提供一个 .yaml 文件。

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

uvicorn main:app –reload

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

Sr.No

Command & Description

1

--host TEXT 将套接字绑定到此主机。[默认 127.0.0.1]

2

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

3

--uds TEXT 绑定到 UNIX 域套接字。

4

--fd INTEGER 从该文件描述符绑定到套接字。

5

--reload Enable auto-reload.

6

--reload-dir PATH 显式设置重新加载目录,默认设置为当前工作目录。

7

--reload-include TEXT 在观察时包含文件。默认情况下包括“*.py”

8

-reload-exclude TEXT 观察时排除文件。

9

--reload-delay FLOAT 前一次和下一次检查之间的延迟,默认值为 0.25

10

*-loop [auto

asyncio

uvloop]*事件循环实现。[默认值为自动]

11

*--http [auto

h11

httptools]*HTTP 协议实现。[默认值为自动]

12

*--interface auto

asgi

asgi

wsgi*选择应用程序界面。[默认值为自动]

13

--env-file PATH Environment configuration file.

14

--log-config PATH 日志记录配置文件。支持以下格式:.ini、.json、.yaml。

15

--version 显示 uvicorn 版本并退出。

16

--app-dir TEXT 在指定的目录查找 APP,默认值为当前目录

17

除了从命令行启动 Uvicorn 服务器外,还可以通过编程方式启动它。

Example

在 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)

现在作为 Python 脚本运行此 app.py ,如下所示 −

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

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

FastAPI - Type Hints

FastAPI 大量使用了 Python 3.5 及更高版本中提供的 Type 提示功能。事实上,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 这样的基本编码环境也表明该函数需要两个参数,但不会指定类型,因为它们尚未声明。

hint1

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

hint2

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

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

hint3

然而,如前所述,如果将不兼容的值传递给函数或由函数返回,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)

函数的第二次和第三次调用中存在错误。在第二次调用中,传递给 y 的值是 float ,而预期是 int 。在第三次调用中,传递给 x 的值是 str ,而预期是 int 。(请注意 // 操作符返回整数除法)

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

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 及更高版本)标准库的新版本中新增了 typing 模块。它为相应的标准集合类型定义了特殊类型。typing 模块上的类型为 List, Tuple, Dict, and Sequence 。它还包含 UnionOptional 类型。请注意,数据类型标准名称全部小写,而 typing 模块中的名称首字母大写。使用此功能,我们可以询问特定类型的集合。

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 Support

Python 的类型提示功能在几乎所有 IDEs (集成开发环境)中(例如 PyCharmVS Code )中都得到了最有效的利用,可以提供动态自动完成功能。

让我们看看 VS Code 如何使用类型提示,在编写代码时提供自动完成功能。在下面的示例中,定义了一个名为 sayhello 的函数,名称作为参数。该函数通过给 name 参数添加一个空格,将“Hello”与其连接,从而返回一个字符串。此外,还需要保证名称的首字母大写。

Python 的 str 类有一个 capitalize() 方法用于此目的,但如果在键入代码时不记得,就需要到其他地方搜索它。如果在名称后面加点,就会看到属性列表,但没有显示任何内容,因为 Python 不知道 name 变量的运行时类型。

ide1

这里,类型提示就派上用场了。将 str 作为函数定义中的 name 类型包含进去。现在,当在 name 后面按点 (.) 时,就会出现一个包含所有字符串方法的下拉列表,从中可以选择所需的方法(在本例中为 capitalize())。

ide2

也可以对用户定义的类使用类型提示。在以下示例中,定义了一个矩形类,其中包含传递给 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 编辑器的屏幕截图。

ide4

FastAPI 大量使用了类型提示。此功能无处不在,如路径参数、查询参数、标头、正文、依赖关系等,以及验证来自传入请求的数据。OpenAPI 文档生成也使用类型提示。

FastAPI - REST Architecture

RElational State Transfer (REST) 是一种软件架构风格。REST 定义 Web 应用程序架构应如何运行。它是一种基于资源的架构,REST 服务器托管的一切(文件、图像或数据库表中的行)都是资源,具有许多表示形式。

REST 建议某些架构约束。

  1. Uniform interface

  2. Statelessness

  3. Client-server

  4. Cacheability

  5. Layered system

  6. Code on demand

REST 约束有以下优点 −

  1. Scalability

  2. Simplicity

  3. Modifiability

  4. Reliability

  5. Portability

  6. Visibility

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

FastAPI - Path Parameters

现代 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 动词可用作操作。

Sr.No.

Method & Description

1

GET 以未加密形式向服务器发送数据。最常见的方法。

2

HEAD 与 GET 相同,但没有响应主体。

3

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

4

PUT 用上传的内容替换目标资源的所有当前表示形式。

5

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

函数定义中的 async 关键字告诉 FastAPI 它应异步运行,即不阻塞当前执行线程。但是,也可以不带 async 前缀定义路径操作函数。

此装饰器函数返回 JSON 响应。虽然它可以返回 Python 的几乎任何对象,但它会自动转换为 JSON。在本教程后面,我们将看到此类函数如何返回 Pydantic 模型对象。

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

Example

在路由中添加另一个具有可变参数的路径装饰器,并将 hello() 函数绑定到具有 name 参数。按照以下方式修改 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 服务器并访问 [role="bare"] [role="bare"]http://localhost:8000/hello/Tutorialspoint URL。浏览器会显示以下 JSON 响应。

{"name":"Tutorialspoint"}

将可变路径参数更改为其他内容,例如 [role="bare"] [role="bare"]http://localhost:8000/hello/Python ,以便浏览器显示 −

{"name":"Python"}

Check OpenAPI docs

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

path1

然后,它将显示 Curl 命令、 request URL 以及服务器响应的详细信息,包括响应主体和响应头。

path2

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

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

在这种情况下, /hello 是路由,后面跟两个用花括号括起来的参数。如果浏览器地址栏中给出的 URL 为 [role="bare"] [role="bare"]http://localhost:8000/hello/Ravi/20 ,则 Ravi 和 20 的数据将分别分配给变量 name 和 age。浏览器显示以下 JSON 响应 −

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

Path Parameters with Types

可以对要装饰的函数的参数使用 Python 的类型提示。在这种情况下,将 name 定义为 str,将 age 定义为 int。

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

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

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

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

path3

FastAPI - Query Parameters

将请求数据传递给服务器的一个经典方法是在 URL 中附加一个查询字符串。假设服务器上 CGI 执行一个 Python 脚本 (hello.py),则用和号 (&) 连接的关键值对列表构成了查询字符串,将它附加到 URL 中的方法是使用问号 (?) 作为分隔符。比如 −

URL 中 (?) 之后的部分是查询字符串,服务器端脚本会进一步解析它进行处理。

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

Example

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

启动 Uvicorn 服务器并在浏览器中使用此 URL −

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

query1

单击 Try it out 按钮,输入 "Ravi" 和 "20" 作为值,然后按 Execute 按钮。文档页面现在显示了 Curl 命令、请求 URL 以及 HTTP 响应的主体和标头。

query2

Example

可以对要装饰的函数的参数使用 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}

尝试输入 [role="bare"] [role="bare"]http://localhost:8000/docs 作为 URL。这会打开 Swagger UI(OpenAPI)文档。参数 'name' 是路径参数,'age' 是查询参数。

query3

FastAPI - Parameter Validation

可以在 URL 的路径参数以及查询参数中应用 validation conditions 。要对路径参数应用验证条件,您需要导入 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 的参数(如 ([role="bare"] [role="bare"]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 文档还显示应用的验证 −

valid1

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

  1. gt − greater than

  2. ge − 大于或等于

  3. lt − less than

  4. 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}

在这种情况下,验证规则应用于 name 和 age 这两个参数。如果输入的 URL 是 [role="bare"] [role="bare"]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 文档也会识别这些约束。

valid2

查询参数还可以对它们应用验证规则。您必须将它们指定为 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 是 [role="bare"] [role="bare"]http://localhost:8000/hello/Ravi/20?percent=79 ,那么浏览器将会显示如下 JSON 响应 −

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

FastAPI 正确地将 percent 识别为带验证条件的查询参数。它反映在 OpenAPI 文档中,如下所示 −

valid3

虽然客户端可以使用 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 类的属性使用类型提示声明。请注意 subjects 属性是 typing 模块定义的 List 类型和内置 list 类型的。

我们可以使用与匹配结构相同的字典填充 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”属性应用 Field 类型,如下所示:

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] = []

按如下所示填充数据。此处的名称超出了 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 模型可以用来映射到 ORM 模型,如 SQLAlchemyPeewee

FastAPI - Request Body

我们现在将使用 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 服务器并通过访问 [role="bare"] [role="bare"]http://localhost:8000/docs 在浏览器中打开 Swagger UI 文档。

文档会识别出 "/students/" 路由使用 POST 方法附加到 student_data() 函数。在 schemas 部分,Student 模型会被列出。

request1

展开其正前方的节点以揭示模型结构。

request2

单击 Try it out 按钮以在请求主体中填入测试值。

request3

单击 Execute 按钮以获取服务器的响应值。

request4

虽然 Pydantic 模型会自动填充请求主体,但也可以使用单值向其添加属性。为此,我们需要使用 Body 类对象作为要进行装饰的操作函数的参数。

首先,我们需要从 fastapi 导入 Body 类。如以下示例中所示,在 @app.post() 装饰符下,将 'name' 和 'marks' 声明为 student_data() 函数定义中的 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 方法,并且有一个带有两个参数的请求主体。

request5

还可以声明一个操作函数具有路径和/或查询参数以及请求体。让我们修改 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

该函数添加 college 和 age 参数的值以及 Student 对象的字典表示形式,并将其作为响应返回。我们可以按照如下方式检查 API 文档:

request6

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

FastAPI - Templates

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

在以下示例中,@app.get() 装饰器的“/hello/”端点和 HTMLResponse 为 response_class。在 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。

template1

请求 URL ([role="bare"] [role="bare"]http://localhost:8000/hello/ ) 也应该在浏览器中呈现该消息。但是,呈现原始 HTML 响应非常繁琐。或者,可以将预先构建的 HTML 页面呈现为模板。为此,我们需要使用 Web 模板库。

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

pip3 install jinja2

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

from fastapi.templating import Jinja2Templates

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

templates = Jinja2Templates(directory="templates")

一个简单的 Web 页面 ‘hello.html’ 来呈现 Hello World 消息也放在 ‘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 响应。当我们启动服务器并访问 [role="bare"] [role="bare"]http://localhost:8000/hello/ URL 时,我们将在浏览器中看到 Hello World 消息,它实际上是 hello.html 的输出。

template2

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

  1. {% %} – Statements

  2. {{ }} - 输送到模板输出的表达式

  3. {# #} - 未包含在模板输出中的注释

  4. # # # - 行语句

hello.html 已修改为如下内容,用于通过替换名称参数来显示动态消息。

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

操作函数 hello() 也已修改为接受名称作为路径参数。 TemplateResponse 还应包括 “name”:name 的 JSON 表示和请求上下文。

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})

重新启动服务器,并转到 [role="bare"] [role="bare"]http://localhost:8000/hello/Kiran 。浏览器现在用此 URL 中的路径参数填充 jinja2 占位符。

template3

FastAPI - Static Files

通常,需要在模板响应中包括一些资源,即便存在某些动态数据,这些资源也不会发生改变。此类资源称为静态资源。媒体文件(.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")

Example

在以下示例中,FastAPI 徽标准备在 hello.html 模板中呈现。因此,“fa-logo.png” 文件首先放在 static 文件夹中。现在,可以将其用作 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 如 [role="bare"] [role="bare"]http://localhost/hello/Vijay 。徽标显示在浏览器窗口中,如图所示。

static1

Example

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

<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 并重新启动服务器。浏览器应显示它下面的当前时间和相应的消息。

static2
static3

FastAPI - HTML Form Templates

让我们在应用程序中添加另一路由 "/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>

请注意,action 参数被设置为“/submit”路由,而 action 设置为 POST。这对于进一步讨论十分重要。

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

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

URL [role="bare"] [role="bare"]http://localhost:8000/login 将呈现登录表单,如下所示:

form1

FastAPI - Accessing Form Data

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

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.

access1

甚至可以使用 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 - Uploading Files

首先,要向服务器发送文件,需要使用 HTML 表单的 enctype 作为 multipart/form-data ,并使用 input 类型作为文件,以便呈现一个按钮,单击该按钮时允许您从文件系统中选择一个文件。

<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>

请注意,表单的 action 参数对应端点 [role="bare"] [role="bare"]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})

访问 [role="bare"] [role="bare"]http://localhost:8000/upload/ 。您应获取带有 Choose File 按钮的表单。单击它以打开要上传的文件。

upload1

上传操作由 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 将收到的文件复制到服务器位置

cookie 是 HTTP 标头之一。除了请求的数据之外,Web 服务器还会向客户端发送响应,它还会插入一个或多个 Cookie。 cookie 是一小部分数据,该数据存储在客户端计算机上。在同一客户端的后续连接请求中,此 Cookie 数据也会附在 HTTP 请求中。

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

在 FastAPI 中,Cookie 参数在响应对象上借助 set_cookie() 方法设置。

response.set_cookie(key, value)

Example

以下是 set_cookie() 方法的示例。我们有一个名为 content 的 JSON 响应对象。调用其上的 set_cookie() 方法,以设置 key="usrname"value="admin" 作为 Cookie−

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。

cookie1

当执行 read_cookie() 函数时,将回读 Cookie 并显示为响应。还需要注意,该文档将用户名标识为 Cookie 参数。

cookie2

FastAPI - Header Parameters

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

在以下示例中,需要检索“接受语言”头部。由于 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 文档显示,检索到的头部显示为响应正文。

header1

你可以在响应对象中推送自定义头部以及预定义头部。 T*he operation function should have a parameter of *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)

新添加的头部将显示在文档的响应头部部分。

header2

FastAPI - Response Model

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

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

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

Example

在下面的示例中,POST 操作装饰器以 student 类的对象(BaseModel 的子类)的形式接收请求主体。由于此类中的一个字段,即 marks(分数列表)在响应中不需要,因此我们定义另一个模型,称为 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" 路由获取 student 类的对象作为请求正文。使用适当的值填充属性并执行 get_percent() 函数。

model1

由于服务器响应已用作 response_model,因此将其转换成 percent 类。

model2

FastAPI - Nested Models

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

Example

在下面的示例中,我们构造一个 Customer 模型,其中一个特性能作为 product 模型类。product 模型反过来具有 supplier 类的属性。

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 操作装饰器将 customer 模型的对象渲染为服务器响应。

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

Swagger UI 页面显示了三个对应于三个 BaseModel 类别的模式。

nested1

Customer 模式在展开以显示所有节点时如下所示 −

nested2

"/invoice" 路由的一个示例响应应该是这样的 −

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

FastAPI - Dependencies

FastAPI 的内置依赖注入系统使得在构建 API 时能够更轻松地集成功能。在编程中, Dependency injection 指的是对象接收它所依赖的其他对象的一种机制。其他对象称为依赖项。依赖项注入具有如下优势 −

  1. 重用相同的共享逻辑

  2. share database connections

  3. 强制进行认证和安全特性

假设一个 FastAPI app 有两个操作函数,这两个函数都带有相同的查询参数 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)):
   return dep
@app.get("/admin/")
async def admin(dep: dependency = Depends(dependency)):
   return dep

此处,我们在操作函数中使用了依赖注入。它也可以用作操作装饰。例如,我们希望检查查询参数 age 的值是否小于 21。如果是,则应该抛出一个异常。因此,我们写一个函数来检查它并用作依赖项。

async def validate(dep: dependency = Depends(dependency)):
   if dep.age > 18:
      raise HTTPException(status_code=400, detail="You are not eligible")
@app.get("/user/", dependencies=[Depends(validate)])
async def user():
   return {"message": "You are eligible"}

在 FastAPI 依赖管理中,你可以使用 yield 而不是 return 来添加一些额外的步骤。例如,以下函数将 yield 用作数据库依赖项。

async def get_db():
   db = DBSession()
      try:
         yield db
      finally:
            db.close()

FastAPI - CORS

Cross-Origin Resource Sharing (CORS) 是当一个运行在一个客户浏览器上的前端应用程序尝试通过 JavaScript 代码与后端进行通信时的情况,并且后端处于与前端不同的“源”中。这里的源是协议、域名和端口号的组合。结果,[role="bare"] [role="bare"]http://localhost 和 [role="bare"] [role="bare"]https://localhost 具有不同的来源。

如果具有一个来源的 URL 的浏览器发送请求以执行来自另一个来源的 JavaScript 代码,则浏览器将发送一个 OPTIONS HTTP 请求。

如果后端通过发送适当的标题来授权来自这种不同来源的通信,它将允许前端中的 JavaScript 向后端发送其请求。为此,后端必须有一个“允许的来源”列表。

若要显式指定允许的来源,请导入 CORSMiddleware 并将来源列表添加到应用程序的中间件。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
   "http://192.168.211.:8000",
   "http://localhost",
   "http://localhost:8080",
]
app.add_middleware(
   CORSMiddleware,
   allow_origins=origins,
   allow_credentials=True,
   allow_methods=["*"],
   allow_headers=["*"],
)
@app.get("/")
async def main():
   return {"message": "Hello World"}

FastAPI - CRUD Operations

REST 架构使用用于资源操作的 HTTP 动作或方法。POST、GET、PUT 和 DELETE 方法分别执行 CREATE、READ、UPDATE 和 DELETE 操作。

在以下示例中,我们将使用 Python 列表作为内存数据库,并对其执行 CRUD 操作。首先,让我们设置一个 FastAPI 应用程序对象,并声明一个名为 Book 的 Pydantic 模型。

from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
data = []
class Book(BaseModel):
   id: int
   title: str
   author: str
   publisher: str

使用 @app.post() 装饰器填充此模型的一个对象,并将其附加到书籍列表(已为书籍列表声明数据)

@app.post("/book")
def add_book(book: Book):
   data.append(book.dict())
   return data

在 Swagger UI 中,执行此操作函数几次并添加一些数据。

crud1

服务器的 JSON 响应显示了到目前为止添加的书籍列表。

crud2

要检索列表,请定义绑定 @app.get() 装饰器的操作函数,如下所示:

@app.get("/list")
def get_books():
   return data

要检索 ID 为路径参数的书籍,请定义 get() 操作装饰器和 get_book() 函数,如下所示:

@app.get("/book/{id}")
def get_book(id: int):
   id = id - 1
   return data[id]

/list 路由检索所有书籍。

crud3

另一方面,在 "/book/1" 路由中使用 "id" 作为路径参数。

crud4

可以在 Swagger UI 的服务器响应中看到,将检索到 "id=1" 的书籍

crud5

接下来,定义 @app.put() 装饰器来修改数据列表中的一个对象。此装饰器也有一个用于 id 字段的路径参数。

@app.put("/book/{id}")
def add_book(id: int, book: Book):
   data[id-1] = book
   return data

在 swagger UI 中检查此操作函数。提供 id=1,并在请求正文中将 publisher 的值更改为 BPB。

crud6

执行时,响应将显示列表,其中 id=1 的对象已更新为新值。

crud7

最后,我们定义了 @app.delete() 装饰器来删除与路径参数对应的对象。

@app.delete("/book/{id}")
def delete_book(id: int):
   data.pop(id-1)
   return data

将 id=1 作为路径参数并执行函数。

crud8

执行后,该列表现在只显示两个对象

crud9

FastAPI - SQL Databases

在上一章中,Python 列表已用作内存中数据库,以使用 FastAPI 执行 CRUD 操作。相反,我们可以使用任何关系数据库(例如 MySQL、Oracle 等)来执行存储、检索、更新和删除操作。

我们不会使用 DB-API 合规的数据库驱动程序,而会使用 SQLAlchemy 作为 Python 代码和数据库之间的界面(我们将使用 SQLite 数据库,因为 Python 已内置对它的支持)。SQLAlchemy 是一个流行的 SQL 工具包和 Object Relational Mapper

对象关系映射是一种在面向对象编程语言中不同类型系统之间转换数据的编程技术。通常,Python 等面向对象语言中使用的类型系统包含非标量类型。然而,大多数数据库产品(例如 Oracle、MySQL 等)中的数据类型都是原始类型,例如整数和字符串。

在 ORM 系统中,每个类都映射到底层数据库中的一个表。ORM 负责处理这些问题,让你专注于对系统逻辑进行编程,而不再需要自己编写乏味的数据库接口代码。

为了使用 SQLAlchemy,我们需要首先使用 PIP 安装程序安装此库。

pip install sqlalchemy

SQLAlchemy 被设计为使用专为某个特定数据库而构建的 DBAPI 实现来运行。它使用方言系统与各种类型的 DBAPI 实现和数据库通信。所有方言都要求已安装相应的 DBAPI 驱动程序。

以下是包含的方言:

  1. Firebird

  2. Microsoft SQL Server

  3. MySQL

  4. Oracle

  5. PostgreSQL

  6. SQLite

  7. Sybase

由于我们需要使用 SQLite 数据库,因此我们需要为我们的数据库创建名为 test.db 的数据库引擎。从 sqlalchemy 模块中导入 create_engine() 函数。

from sqlalchemy import create_engine
from sqlalchemy.dialects.sqlite import *
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args = {"check_same_thread": False})

为了与数据库交互,我们需要获取其句柄。会话对象是数据库的句柄。会话类使用 sessionmaker() − 可配置会话工厂方法(绑定到引擎对象)来定义。

from sqlalchemy.orm import sessionmaker, Session
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)

接下来,我们需要一个声明式基类,用于在声明式系统中存储类的目录和映射的表。

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

BooksBase 的子类,映射到数据库中的 book 表。 Books 类中的属性对应于目标表中列的数据类型。请注意,id 属性对应于 book 表中的主键。

from sqlalchemy import Column, Integer, String
class Books(Base):
   __tablename__ = 'book'
   id = Column(Integer, primary_key=True, nullable=False)
   title = Column(String(50), unique=True)
   author = Column(String(50))
   publisher = Column(String(50))
   Base.metadata.create_all(bind=engine)

create_all() 方法在数据库中创建相应的表。

我们现在必须声明一个与声明性基本子类相对应的 Pydantic 模型(上面定义的 Books 类)。

from typing import List
from pydantic import BaseModel, constr
class Book(BaseModel):
   id: int
   title: str
   author:str
   publisher: str
   class Config:
      orm_mode = True

请注意 config 类中使用了 orm_mode=True ,表示它已与 SQLAlchemy 的 ORM 类进行映射。

其余代码与内存中 CRUD 操作类似,不同之处在于操作函数通过 SQLalchemy 接口与数据库交互。下面定义了 FastAPI 应用程序对象上的 POST 操作−

from fastapi import FastAPI, Depends
app=FastAPI()
def get_db():
   db = session()
   try:
      yield db
   finally:
   db.close()
@app.post('/add_new', response_model=Book)
def add_book(b1: Book, db: Session = Depends(get_db)):
   bk=Books(id=b1.id, title=b1.title, author=b1.author,
publisher=b1.publisher)
   db.add(bk)
   db.commit()
   db.refresh(bk)
   return Books(**b1.dict())

首先建立一个数据库会话。将 POST 请求体中的数据作为新行添加到 book 表中。执行 add_book() 操作函数以将示例数据添加到 books 表中。为了验证,你可以使用 SQLiteStudio,这是一个适用于 SQLite 数据库的 GUI 工具。

sql1

定义了 GET 操作的两个操作函数,一个用于获取所有记录,另一个用于匹配路径参数的记录。

以下是绑定到 /list 路由的 get_books() 函数。执行后,其服务器响应是所有记录的列表。

@app.get('/list', response_model=List[Book])
def get_books(db: Session = Depends(get_db)):
   recs = db.query(Books).all()
   return recs

/book/{id} 路由使用 id 作为路径参数调用 get_book() 函数。SQLAlchemy 的查询返回一个对应于给定 id 的对象。

@app.get('/book/{id}', response_model=Book)
def get_book(id:int, db: Session = Depends(get_db)):
   return db.query(Books).filter(Books.id == id).first()

下图显示了从 Swagger UI 执行的 get_books() 函数的结果。

sql2

更新和删除操作由 update_book() 函数执行(当访问 /update/{id} 路由时执行),由 del_book() 函数执行(当在 URL 中提供 /delete/{id} 路由时调用)。

@app.put('/update/{id}', response_model=Book)
def update_book(id:int, book:Book, db: Session = Depends(get_db)):
   b1 = db.query(Books).filter(Books.id == id).first()
   b1.id=book.id
   b1.title=book.title
   b1.author=book.author
   b1.publisher=book.publisher
   db.commit()
   return db.query(Books).filter(Books.id == id).first()
@app.delete('/delete/{id}')
def del_book(id:int, db: Session = Depends(get_db)):
   try:
      db.query(Books).filter(Books.id == id).delete()
      db.commit()
   except Exception as e:
      raise Exception(e)
   return {"delete status": "success"}

如果你打算使用任何其他数据库来代替 SQLite,你只需要相应地更改方言定义。例如,要使用 MySQL 数据库和 pymysql 驱动程序,请将引擎对象的语句更改为以下内容−

engine = create_engine('mysql+pymysql://user:password@localhost/test')

FastAPI - Using MongoDB

FastAPI 还可以使用 NoSQL 数据库(如 MongoDB、Cassandra、CouchDB 等)作为 REST 应用程序的 CRUD 操作的后端。在本主题中,我们将了解如何在 FastAPI 应用程序中使用 MongoDB。

MongoDB 是面向文档的数据库,其中半结构化文档存储为 JSON 等格式。文档可以包含许多不同的键值对、键数组对,甚至嵌套文档。它是键值对的集合,类似于 Python 字典对象。一个或多个此类文档存储在一个集合中。

MongoDB 中的集合等同于关系数据库中的表。但是,MongoDB(与所有 NoSQL 数据库一样)没有预定义的模式。文档类似于基于 SQL 的关系数据库表中的单行。每个文档可能具有可变数量的键值对。因此,MongoDB 是无模式数据库。

要将 MongoDB 与 FastAPI 配合使用,必须在计算机上安装 MongoDB 服务器。我们还需要安装 PyMongo ,这是 MongoDB 的官方 Python 驱动程序。

pip3 install pymongo

在通过 Python 和 FastAPI 代码与 MongoDB 数据库交互之前,确保通过发出以下命令来运行 MongoDB(假设 MongoDB 服务器安装在 e:\mongodb 文件夹中)。

E:\mongodb\bin>mongod
..
waiting for connections on port 27017

PyMongo 模块中的 MongoClient 类的对象是 Python 用于与 MongoDB 服务器交互的句柄。

from pymongo import MongoClient
client=MongoClient()

我们定义 Book 作为 BaseModel 类来填充请求正文(与 SQLite 示例中使用的类相同)

from pydantic import BaseModel
from typing import List
class Book(BaseModel):
   bookID: int
   title: str
   author:str
   publisher: str

设置 FastAPI 应用对象 −

from fastapi import FastAPI, status
app = FastAPI()

POST 操作装饰器将 "/add_new" 作为 URL 路由,并执行 add_book() 函数。它将 Book BaseModel 对象解析为字典,并在测试数据库的 BOOK_COLLECTION 中添加文档。

@app.post("/add_new", status_code=status.HTTP_201_CREATED)
def add_book(b1: Book):
   """Post a new message to the specified channel."""
   with MongoClient() as client:
      book_collection = client[DB][BOOK_COLLECTION]
      result = book_collection.insert_one(b1.dict())
      ack = result.acknowledged
      return {"insertion": ack}

通过访问 [role="bare"]http://localhost:8000/docs ,使用 Swagger UI 的 Web 界面添加几个文档。您可以在 MongoDB 的 Compass GUI 前端中验证集合。

mongo1

要检索所有书籍的列表,让我们包含以下 get 操作函数 * − get_books() . It will be executed when *"/books" 访问 URL 路由。

@app.get("/books", response_model=List[str])
def get_books():
   """Get all books in list form."""
   with MongoClient() as client:
      book_collection = client[DB][BOOK_COLLECTION]
      booklist = book_collection.distinct("title")
      return booklist

在这种情况下,服务器响应将是书籍集合中所有标题的列表。

[
   "Computer Fundamentals",
   "Python Cookbook",
   "Let Us Python"
]

以下 GET 装饰器检索与给定 ID 作为路径参数对应的书籍文档 −

@app.get("/books/{id}", response_model=Book)
def get_book(id: int):
   """Get all messages for the specified channel."""
   with MongoClient() as client:
      book_collection = client[DB][BOOK_COLLECTION]
      b1 = book_collection.find_one({"bookID": id})
      return b1

Swagger UI 文档页面显示以下界面 −

mongo2

当执行上述函数时,服务器的 JSON 响应如下 −

mongo3

FastAPI - Using GraphQL

Facebook 于 2012 年开发了 GraphQL ,这是一个新的 API 标准,旨在优化 RESTful API 调用。GraphQL 是 API 的数据查询和操作语言。与 REST 相比,GraphQL 更灵活、更高效、更准确。GraphQL 服务器仅提供一个单一端点,并响应客户端所需的确切数据。

由于 GraphQL 与 ASGI 兼容,因此可以轻松地将其与 FastAPI 应用程序集成。有许多适用于 GraphQL 的 Python 库。其中一些列在下面 -

  1. Strawberry

  2. Ariadne

  3. Tartiflette

  4. Graphene

FastAPI 的官方文档建议使用 Strawberry 库,因为它的设计也是基于类型注释(就像 FastAPI 本身一样)。

为了将 GraphQL 与 FastAPI 应用程序集成,首先将 Python 类装饰为 Strawberry 类型。

@strawberry.type
class Book:
   title: str
   author: str
   price: int

接下来,声明一个 Query 类,其中包含一个返回 Book 对象的函数。

@strawberry.type
class Query:
   @strawberry.field
   def book(self) -> Book:
   return Book(title="Computer Fundamentals", author="Sinha", price=300)

使用此 Query 类作为参数来获取 Strawberry.Schema 对象

schema = strawberry.Schema(query=Query)

然后声明 GraphQL 类和 FastAPI 应用程序类的对象。

graphql_app = GraphQL(schema)
app = FastAPI()

最后,向 FastAPI 对象添加路由并运行服务器。

app.add_route("/book", graphql_app)
app.add_websocket_route("/book", graphql_app)

在浏览器中访问 [role="bare"] [role="bare"]http://localhost:8000/book 。一个内置浏览器的 GraphQL IDE 将被打开。

graph1

在注释部分的下面,使用 Graphiql IDE 的 Explorer 栏输入以下查询。运行查询以在输出窗格中显示结果。

graph2

FastAPI - Websockets

WebSocket 是一个客户端和服务器之间的持久连接,用于在两者之间提供双向的 full-duplex 通信。通信通过单个 TCP/IP 套接字连接通过 HTTP 发生。它可以看作是 HTTP 的升级,而不是协议本身。

HTTP 的局限性之一是它是一个严格的半双工或单向协议。另一方面,借助 WebSocket,我们可以发送基于消息的数据(类似于 UDP),但借助 TCP 的可靠性。WebSocket 使用 HTTP 作为初始传输机制,但在收到 HTTP 响应后保持 TCP 连接的活动状态。可以将同一连接对象用于客户端和服务器之间的双向通信。因此,可以使用 WebSocket API 构建实时应用。

FastAPI 通过 FastAPI 模块中的 WebSocket 类支持 WebSocket。以下示例演示了在 FastAPI 应用程序中 WebSocket 的功能。

首先,我们有一个 index() 函数来呈现一个模板 (socket.html)。它绑定到 “/” 路由。HTML 文件 socket.html 放置在 “templates” 文件夹中。

main.py

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

模板文件呈现一个文本框和一个按钮。

socket.html

<!DOCTYPE html>
<html>
   <head>
      <title>Chat</title>
      <script src="{{ url_for('static', path='ws.js') }}"></script>
   </head>
   <body>
      <h1>WebSocket Chat</h1>
      <form action="" onsubmit="sendMessage(event)">
      <input type="text" id="messageText" autocomplete="off"/>
      <button>Send</button>
      </form>
      <ul id='messages'>
      </ul>
   </body>
</html>

在 socket.html 内部,有一个对 JavaScript 函数的调用,该函数将在表单提交时执行。因此,为了提供 JavaScript 服务,“static” 文件夹首先被挂载。JavaScript 文件 ws.js 放置在 “static” 文件夹中。

ws.js

var ws = new WebSocket("ws://localhost:8000/ws");
ws.onmessage = function(event) {
   var messages = document.getElementById('messages')
   var message = document.createElement('li')
   var content = document.createTextNode(event.data)
   message.appendChild(content)
   messages.appendChild(message)
};
function sendMessage(event) {
   var input = document.getElementById("messageText")
   ws.send(input.value)
   input.value = ''
   event.preventDefault()
}

加载 JavaScript 代码时,它创建一个套接字,在 “ws://localhost:8000/ws” 处进行监听。 sendMessage() 函数将输入消息定向到 WebSocket URL。

此路由调用应用程序代码中的 websocket_endpoint() 函数。接受传入的连接请求,并在客户端浏览器上显示传入的消息。将以下代码添加到 main.py 中。

from fastapi import WebSocket
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
   await websocket.accept()
   while True:
      data = await websocket.receive_text()
      await websocket.send_text(f"Message text was: {data}")

保存 FastAPI 代码文件 (main.py)、模板 (socket.html) 和 JavaScript 文件 (ws.js)。运行 Uvicorn 服务器并访问 [role="bare"] [role="bare"]http://localhost:8000/ 以呈现如下所示的聊天窗口 −

web1

键入某个文本并按下发送按钮。输入消息将通过 WebSocket 重定向到浏览器上。

web2

FastAPI - FastAPI Event Handlers

事件处理程序是在某个已识别的事件发生时要执行的函数。在 FastAPI 中,识别出两个这样的事件 * − 启动* 和 shutdown 。FastAPI 的应用程序对象具有 on_event() 装饰器,该装饰器使用这些事件中的一个作为参数。使用此装饰器注册的函数在发生相应事件时触发。

启动事件在开发服务器启动之前发生,并且通常使用注册函数执行某些初始化任务,例如与数据库建立连接等。关闭事件的事件处理程序在应用程序关闭之前调用。

Example

以下是启动和关闭事件处理程序的一个简单示例。当应用程序启动时,启动时间会显示在控制台日志中。同样,当通过按下 ctrl+c 停止服务器时,也会显示关闭时间。

main.py

from fastapi import FastAPI
import datetime
app = FastAPI()
@app.on_event("startup")
async def startup_event():
   print('Server started :', datetime.datetime.now())
@app.on_event("shutdown")
async def shutdown_event():
   print('server Shutdown :', datetime.datetime.now())

Output

它将生成如下输出:

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.
Server started: 2021-11-23 23:51:45.907691
INFO: Application startup complete.
INFO: Shutting down
INFO: Waiting for application
server Shutdown: 2021-11-23 23:51:50.82955
INFO: Application shutdown com
INFO: Finished server process

FastAPI - Mounting a Sub-App

如果您有 2 个独立的 FastAPI 应用,则可以将其中之一安装到另一个之上。安装的那个被称为子应用程序。 app.mount() 方法在主应用程序的特定路径中添加了另一个完全“独立”的应用程序。然后,它负责处理该路径下的所有内容,包括在该子应用程序中声明的路径操作。

让我们首先声明一个简单的 FastAPI 应用程序对象,用作顶级应用程序。

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

然后创建另一个应用程序对象 subapp,并添加其自己的路径操作。

subapp = FastAPI()
@subapp.get("/sub")
def subindex():
   return {"message": "Hello World from sub app"}

通过使用 mount() 方法将此 subapp 对象安装到主应用程序上。需要的两个参数是 URL 路由和子应用程序的名称。

app.mount("/subapp", subapp)

主应用程序和子应用程序将有自己的文档,可以使用 Swagger UI 来检查它们。

sub1

子应用程序的文档在 [role="bare"] [role="bare"]http://localhost:8000/subapp/docs 处可用

sub2

FastAPI - Middleware

一个 middleware 是一个函数,每次请求(在由任何特定路径操作处理之前)以及每次响应在返回之前都会处理此函数。此函数处理每次到达您的应用程序的请求。它可以通过运行其中定义的代码对请求执行一些处理,然后将该请求传递给相应的操作函数以进行处理。它还可以处理操作函数在返回之前生成的响应。

以下是 FastAPI 库中提供的一些中间件 −

  1. CORSMiddleware

  2. HTTPSRedirectMiddleware

  3. TrustedHostMiddleware

  4. GZipMiddleware

FastAPI 提供 app.add_middleware() 函数来处理服务器错误和自定义异常处理程序。除了上述集成中间件之外,还可以在自定义中间件中定义自定义的中间件。以下示例定义 addmiddleware() 函数并通过 @app.middleware() 装饰器将其装饰到中间件中

该函数有两个参数,HTTP 请求对象和 call_next() 函数,该函数会将 API 请求发送到其对应的路径并返回响应。

除了中间件函数之外,应用程序还有两个操作函数。

import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def addmiddleware(request: Request, call_next):
   print("Middleware works!")
   response = await call_next(request)
   return response
@app.get("/")
async def index():
   return {"message":"Hello World"}
@app.get("/{name}")
async def hello(name:str):
   return {"message":"Hello "+name}

随着应用程序的运行,对于浏览器发出的每个请求,中间件输出(中间件工作!)都会在响应输出之前显示在控制台日志中。

FastAPI - Mounting Flask App

Flask 或 Django 框架中编写的 WSGI 应用程序可以在 WSGIMiddleware 中进行封装,并将其装载到 FastAPI 应用程序上,使其符合 ASGI。

首先在当前 FastAPI 环境中安装 Flask 包。

pip3 install flask

以下代码是一个极简的 Flask 应用程序−

from flask import Flask
flask_app = Flask(__name__)
@flask_app.route("/")
def index_flask():
   return "Hello World from Flask!"

然后,将应用程序声明为 FastAPI 应用程序对象,并定义一个操作函数来呈现 Hello World 消息。

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

接下来,使用 mount() 方法将 flask 应用程序装载为 FastAPI 主应用程序的子应用程序。

from fastapi.middleware.wsgi import WSGIMiddleware
app.mount("/flask", WSGIMiddleware(flask_app))

运行 Uvicorn 开发服务器。

uvicorn flaskapp:app –reload

FastAPI 主应用程序可在 URL [role="bare"] [role="bare"]http://localhost:8000/ 路由中使用。

{"message":"Hello World from FastAPI!"}

Flask 子应用程序装载在 URL [role="bare"] [role="bare"]http://localhost:8000/flask 上。

Hello World from Flask!

FastAPI - Deployment

到目前为止,我们一直在使用本地开发服务器“Uvicorn”运行我们的 FastAPI 应用程序。为了使该应用程序公开可用,必须将其部署到具有静态 IP 地址的远程服务器上。可以使用免费计划或基于订阅的服务将其部署到不同的平台,例如 Heroku、Google Cloud、nginx 等。

在本章中,我们将使用 Deta 云平台。其免费使用的部署服务非常易于使用。

首先,要使用 Deta,我们需要在它的网站上使用合适的用户名和密码创建一个帐户。

deploy1

创建帐户后,在本地计算机上安装 Deta CLI (命令行界面)。为您的应用程序(c:\fastapi_deta_app)创建一个文件夹如果您使用的是 Linux,请在终端中使用以下命令−

iwr https://get.deta.dev/cli.ps1 -useb | iex

如果您使用的是 Windows,请从 Windows PowerShell 终端运行以下命令−

PS C:\fastapi_deta_app> iwr https://get.deta.dev/cli.ps1 -useb | iex
Deta was installed successfully to
C:\Users\User\.deta\bin\deta.exe
Run 'deta --help' to get started

使用登录命令并验证您的用户名和密码。

PS C:\fastapi_deta_app> deta login
Please, log in from the web page. Waiting...
https://web.deta.sh/cli/60836
Logged in successfully.

在同一应用程序文件夹中,在 main.py 文件中创建一个极简的 FastAPI 应用程序

# main.py
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):
   return {"item_id": item_id}

现在我们准备部署我们的应用程序。从 PowerShell 终端中使用 deta new 命令。

PS C:\fastapi_deta_app> deta new
Successfully created a new micro
{
   "name": "fastapi_deta_app",
   "id": "2b236e8f-da6a-409b-8d51-7c3952157d3c",
   "project": "c03xflte",
   "runtime": "python3.9",
   "endpoint": "https://vfrjgd.deta.dev",
   "region": "ap-southeast-1",
   "visor": "enabled",
   "http_auth": "disabled"
}
Adding dependencies...
…..
Installing collected packages: typing-extensions, pydantic, idna, sniffio, anyio, starlette, fastapi
Successfully installed anyio-3.4.0 fastapi-0.70.0 idna-3.3 pydantic-1.8.2 sniffio-1.2.0 starlette-0.16.0 typingextensions-4.0.0

Deta 在给定的端点(可能为每个应用程序随机创建)部署应用程序。它首先安装所需的依赖项,就像在本地计算机上安装一样。成功部署后,打开浏览器并访问端点键前面显示的 URL。 Swagger UI 文档也可以在 [role="bare"] [role="bare"]https://vfrigd.deta.dev/docs 中找到。

deploy2