路径参数 有类型的路径参数 在这个例子中,item_id 被声明为 int 类型。
1 2 3 @app.get("/items/{item_id}" ) async def read_item (item_id:int ): return {"item_id" : item_id}
你可以使用同样的类型声明来声明 str、float、bool 以及许多其他的复合数据类型 所有的数据校验都由 Pydantic 在幕后完成
顺序很重要 1 2 3 4 5 6 7 8 @app.get("/users/me" ) async def read_user_me (): return {"user_id" : "the current user" }@app.get("/users/{user_id}" ) async def read_user (user_id: str ): return {"user_id" : user_id}
由于路径操作是按顺序依次运行的,你需要确保路径 /users/me
声明在路径 /users/{user_id}
之前 否则,/users/{user_id}
的路径还将与 /users/me
相匹配,”认为”自己正在接收一个值为 “me” 的 user_id
参数。
预设值 如果你有一个接收路径参数的路径操作,但你希望预先设定可能的有效参数值 ,则可以使用标准的 Python Enum 类型。
创建一个 Enum 类 1 2 3 4 5 6 from enum import Enumclass ModelName (str , Enum): alexnet = "alexnet" resnet = "resnet" lenet = "lenet"
声明路径参数 1 2 3 4 5 6 7 8 9 10 @app.get("/models/{model_name}" ) async def get_model (model_name: ModelName ): if model_name is ModelName.alexnet: return {"model_name" : model_name, "message" : "Deep Learning FTW!" } if model_name.value == "lenet" : return {"model_name" : model_name, "message" : "LeCNN all the images" } return {"model_name" : model_name, "message" : "Have some residuals" }
包含路径的路径参数 假设你有一个路径操作,它的路径为 /files/{file_path}
但是你需要 file_path
自身也包含路径,比如 home/johndoe/myfile.txt
因此,该文件的URL将类似于这样:/files/home/johndoe/myfile.txt
你可以使用直接来自 Starlette 的选项来声明一个包含路径的路径参数: 而且文档依旧可以使用,但是不会添加任何该参数应包含路径的说明。
在这种情况下,参数的名称为 file_path,结尾部分的 :path 说明该参数应匹配任意的路径。
1 2 3 @app.get("/files/{file_path:path}" ) async def read_file (file_path: str ): return {"file_path" : file_path}
你可能会需要参数包含 /home/johndoe/myfile.txt,以斜杠(/)开头。 在这种情况下,URL 将会是 /files//home/johndoe/myfile.txt,在files 和 home 之间有一个双斜杠(//)。
查询参数 声明不属于路径参数的其他函数参数时,它们将被自动解释为”查询字符串”参数
1 2 3 @app.get("/items/" ) async def read_item (skip: int = 0 , limit: int = 10 ): return fake_items_db[skip : skip + limit]
例如:http://127.0.0.1:8000/items/?skip=0&limit=10
查询参数为:
由于它们是 URL 的一部分,因此它们的”原始值”是字符串。 但是,当你为它们声明了 Python 类型(在上面的示例中为 int)时,它们将转换为该类型并针对该类型进行校验。
默认值 在上面的示例中,它们具有 skip=0 和 limit=10 的默认值。
可选参数 通过同样的方式,你可以将它们的默认值设置为 None 来声明可选查询参数:
1 2 3 4 5 6 7 from typing import Union @app.get("/items/{item_id}" ) async def read_item (item_id: str , q: Union [str , None ] = None ): if q: return {"item_id" : item_id, "q" : q} return {"item_id" : item_id}
在这个例子中,函数参数 q 将是可选的,并且默认值为 None。
查询参数类型转换 你还可以声明 bool 类型,它们将被自动转换:
1 2 3 4 5 6 7 8 9 10 @app.get("/items/{item_id}" ) async def read_item (item_id: str , q: Union [str , None ] = None , short: bool = False ): item = {"item_id" : item_id} if q: item.update({"q" : q}) if not short: item.update( {"description" : "This is an amazing item that has a long description" } ) return item
这个例子中,如果你访问:http://127.0.0.1:8000/items/foo?short=1
或http://127.0.0.1:8000/items/foo?short=True
或http://127.0.0.1:8000/items/foo?short=true
或http://127.0.0.1:8000/items/foo?short=on
或http://127.0.0.1:8000/items/foo?short=yes
或任何其他的变体形式(大写,首字母大写等等),你的函数接收的 short 参数都会是布尔值 True。对于值为 False 的情况也是一样的。
多个路径和查询参数 你可以同时声明多个路径参数和查询参数,FastAPI 能够识别它们。 而且你不需要以任何特定的顺序来声明。 它们将通过名称被检测到:
1 2 3 4 5 6 7 8 9 10 11 12 @app.get("/users/{user_id}/items/{item_id}" ) async def read_user_item ( user_id: int , item_id: str , q: Union [str , None ] = None , short: bool = False ): item = {"item_id" : item_id, "owner_id" : user_id} if q: item.update({"q" : q}) if not short: item.update( {"description" : "This is an amazing item that has a long description" } ) return item
必需查询参数 当你为非路径参数声明了默认值时(目前而言,我们所知道的仅有查询参数),则该参数不是必需的。 如果你不想添加一个特定的值,而只是想使该参数成为可选的,则将默认值设置为 None。 但当你想让一个查询参数成为必需的,不声明任何默认值就可以:
1 2 3 4 @app.get("/items/{item_id}" ) async def read_user_item (item_id: str , needy: str ): item = {"item_id" : item_id, "needy" : needy} return item
这里的查询参数 needy 是类型为 str 的必需查询参数。
当然,你也可以定义一些参数为必需的,一些具有默认值,而某些则完全是可选的:
1 2 3 4 5 6 @app.get("/items/{item_id}" ) async def read_user_item ( item_id: str , needy: str , skip: int = 0 , limit: Union [int , None ] = None ): item = {"item_id" : item_id, "needy" : needy, "skip" : skip, "limit" : limit} return item
在这个例子中,有3个查询参数:
needy,一个必需的 str 类型参数。
skip,一个默认值为 0 的 int 类型参数。
limit,一个可选的 int 类型参数。
请求体 我们使用 Pydantic
模型来声明请求体,并能够获得它们所具有的所有能力和优点。
导入 Pydantic 的 BaseModel 1 from pydantic import BaseModel
创建数据模型 然后,将你的数据模型声明为继承自 BaseModel 的类。 使用标准的 Python 类型来声明所有属性:
1 2 3 4 5 class Item (BaseModel ): name: str description: str | None = None price: float tax: float | None = None
和声明查询参数时一样,当一个模型属性具有默认值时,它不是必需的。否则它是一个必需属性。将默认值设为 None 可使其成为可选属性。
声明为参数 使用与声明路径和查询参数的相同方式声明请求体,即可将其添加到「路径操作」中:
1 2 3 @app.post("/items/" ) async def create_item (item: Item ): return item
结果 仅仅使用了 Python 类型声明,FastAPI 将会:
将请求体作为 JSON 读取。
转换为相应的类型(在需要时)。
校验数据。
如果数据无效,将返回一条清晰易读的错误信息,指出不正确数据的确切位置和内容。
将接收的数据赋值到参数 item 中。
由于你已经在函数中将它声明为 Item 类型,你还将获得对于所有属性及其类型的一切编辑器支持(代码补全等)。
为你的模型生成 JSON 模式 定义,你还可以在其他任何对你的项目有意义的地方使用它们。
这些模式将成为生成的 OpenAPI 模式的一部分,并且被自动化文档 UI 所使用。
使用模型 在函数内部,你可以直接访问模型对象的所有属性:
1 2 3 4 5 6 7 @app.post("/items/" ) async def create_item (item: Item ): item_dict = item.dict () if item.tax: price_with_tax = item.price + item.tax item_dict.update({"price_with_tax" : price_with_tax}) return item_dict
请求体 + 路径参数 你可以同时声明路径参数和请求体。 FastAPI 将识别出与路径参数匹配的函数参数应从路径中获取,而声明为 Pydantic 模型的函数参数应从请求体中获取。
请求体 + 路径参数 + 查询参数 你还可以同时声明请求体、路径参数和查询参数。 FastAPI 会识别它们中的每一个,并从正确的位置获取数据。
函数参数将依次按如下规则进行识别:
如果在路径中也声明了该参数,它将被用作路径参数。
如果参数属于单一类型(比如 int、float、str、bool 等)它将被解释为查询参数。
如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体。
不使用 Pydantic 如果你不想使用 Pydantic 模型,你还可以使用 Body 参数。请参阅文档 请求体 - 多个参数:请求体中的单一值。
查询参数和字符串校验 FastAPI 允许你为参数声明额外的信息和校验。 让我们以下面的应用程序为例:
1 2 3 4 5 6 @app.get("/items/" ) async def read_items (q: str | None = None ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
额外的校验 我们打算添加约束条件:即使 q 是可选的,但只要提供了该参数,则该参数值不能超过50个字符的长度。
1 2 3 4 5 6 7 8 from fastapi import FastAPI, Query@app.get("/items/" ) async def read_items (q: Union [str , None ] = Query(default=None , max_length=50 ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
Query
显式地将q
声明为查询参数。
添加更多校验
默认值 你可以向 Query 的第一个参数传入 None 用作查询参数的默认值,以同样的方式你也可以传递其他默认值。 假设你想要声明查询参数 q,使其 min_length 为 3,并且默认值为 fixedquery:
1 2 3 4 5 6 @app.get("/items/" ) async def read_items (q: str = Query(default="fixedquery" , min_length=3 ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
声明为必需参数 当我们不需要声明额外的校验或元数据时,只需不声明默认值就可以使 q 参数成为必需参数,例如:q: str
当你在使用 Query 且需要声明一个值是必需的时,只需不声明默认参数:q: str = Query(min_length=3)
使用省略号(…)声明必需参数 有另一种方法可以显式的声明一个值是必需的,即将默认参数的默认值设为 … :
1 2 3 4 5 6 @app.get("/items/" ) async def read_items (q: str = Query(default=..., min_length=3 ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
如果你之前没见过 … 这种用法:它是一个特殊的单独值,它是 Python 的一部分并且被称为「省略号」 。 Pydantic 和 FastAPI 使用它来显式的声明需要一个值。
这将使 FastAPI 知道此查询参数是必需的。
使用None声明必需参数 你可以声明一个参数可以接收None值,但它仍然是必需的。这将强制客户端发送一个值,即使该值是None。
1 2 3 4 5 6 @app.get("/items/" ) async def read_items (q: Union [str , None ] = Query(default=..., min_length=3 ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
使用Pydantic中的Required代替省略号(…) 如果你觉得使用 … 不舒服,你也可以从 Pydantic 导入并使用 Required:
1 2 3 4 5 6 7 8 from pydantic import Required@app.get("/items/" ) async def read_items (q: str = Query(default=Required, min_length=3 ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
请记住,在大多数情况下,当你需要某些东西时,可以简单地省略 default 参数,因此你通常不必使用 … 或 Required
查询参数列表 / 多个值 当你使用 Query 显式地定义查询参数时,你还可以声明它去接收一组值,或换句话来说,接收多个值。 例如,要声明一个可在 URL 中出现多次的查询参数 q,你可以这样写:
1 2 3 4 @app.get("/items/" ) async def read_items (q: Union [List [str ], None ] = Query(default=None ) ): query_items = {"q" : q} return query_items
然后,输入如下网址:http://localhost:8000/items/?q=foo&q=bar
你会在路径操作函数的函数参数 q 中以一个 Python list 的形式接收到查询参数 q 的多个值(foo 和 bar)。 因此,该 URL 的响应将会是:
1 2 3 4 5 6 { "q" : [ "foo" , "bar" ] }
要声明类型为 list 的查询参数,如上例所示,你需要显式地使用 Query,否则该参数将被解释为请求体。
具有默认值的查询参数列表 / 多个值 你还可以定义在没有任何给定值时的默认 list 值:
1 2 3 4 @app.get("/items/" ) async def read_items (q: List [str ] = Query(default=["foo" , "bar" ] ) ): query_items = {"q" : q} return query_items
使用 list 你也可以直接使用 list 代替 List [str]:
1 2 3 4 @app.get("/items/" ) async def read_items (q: list = Query(default=[] ) ): query_items = {"q" : q} return query_items
请记住,在这种情况下 FastAPI 将不会检查列表的内容。 例如,List[int] 将检查(并记录到文档)列表的内容必须是整数。但是单独的 list 不会。
声明更多元数据 你可以添加更多有关该参数的信息。 这些信息将包含在生成的 OpenAPI 模式中,并由文档用户界面和外部工具所使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 @app.get("/items/" ) async def read_items ( q: Union [str , None ] = Query( default=None , title="Query string" , description="Query string for the items to search in the database that have a good match" , min_length=3 , ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
别名参数 假设你想要查询参数为 item-query。 像下面这样:http://127.0.0.1:8000/items/?item-query=foobaritems
但是 item-query 不是一个有效的 Python 变量名称。 最接近的有效名称是 item_query。 但是你仍然要求它在 URL 中必须是 item-query… 这时你可以用 alias 参数声明一个别名,该别名将用于在 URL 中查找查询参数值:
1 2 3 4 5 6 @app.get("/items/" ) async def read_items (q: Union [str , None ] = Query(default=None , alias="item-query" ) ): results = {"items" : [{"item_id" : "Foo" }, {"item_id" : "Bar" }]} if q: results.update({"q" : q}) return results
弃用参数 现在假设你不再喜欢此参数。 你不得不将其保留一段时间,因为有些客户端正在使用它,但你希望文档清楚地将其展示为已弃用。 那么将参数 deprecated=True 传入 Query
路径参数和数值校验 与使用 Query 为查询参数声明更多的校验和元数据的方式相同,你也可以使用 Path 为路径参数声明相同类型的校验和元数据。
声明元数据 你可以声明与 Query 相同的所有参数。 例如,要声明路径参数 item_id的 title 元数据值,你可以输入:
1 2 3 4 5 6 7 8 9 10 11 12 from fastapi import FastAPI, Path, Queryfrom typing import Annotated@app.get("/items/{item_id}" ) async def read_items ( item_id: Annotated[int , Path(title="The ID of the item to get" )], q: Annotated[str | None , Query(alias="item-query" )] = None , ): results = {"item_id" : item_id} if q: results.update({"q" : q}) return results
按需对参数排序 如果你将带有「默认值」的参数放在没有「默认值」的参数之前,Python 将会报错。 但是你可以对其重新排序,并将不带默认值的值(查询参数 q)放到最前面。
1 2 3 4 5 6 @app.get("/items/{item_id}" ) async def read_items (q: str , item_id: int = Path(title="The ID of the item to get" ) ): results = {"item_id" : item_id} if q: results.update({"q" : q}) return results
如果你想不使用 Query 声明没有默认值的查询参数 q,同时使用 Path 声明路径参数 item_id,并使它们的顺序与上面不同,Python 对此有一些特殊的语法。 传递 * 作为函数的第一个参数。 Python 不会对该 * 做任何事情,但是它将知道之后的所有参数都应作为关键字参数(键值对),也被称为 kwargs,来调用。即使它们没有默认值。
1 2 3 4 5 6 @app.get("/items/{item_id}" ) async def read_items (*, item_id: int = Path(title="The ID of the item to get" ), q: str ): results = {"item_id" : item_id} if q: results.update({"q" : q}) return results
数值校验 使用 Query 和 Path(以及你将在后面看到的其他类)可以声明字符串约束,但也可以声明数值约束。
1 2 3 4 5 6 7 8 @app.get("/items/{item_id}" ) async def read_items ( *, item_id: int = Path(title="The ID of the item to get" , ge=1 ), q: str ): results = {"item_id" : item_id} if q: results.update({"q" : q}) return results
gt:大于(g
reater t
han)
ge:大于等于(g
reater than or e
qual)
lt:小于(l
ess t
han)
le:小于等于(l
ess than or e
qual)
请求体 - 多个参数 混合使用 Path、Query 和请求体参数 首先,毫无疑问地,你可以随意地混合使用 Path、Query 和请求体参数声明,FastAPI 会知道该如何处理。 你还可以通过将默认值设置为 None 来将请求体参数声明为可选参数
1 2 3 4 5 6 7 @app.put("/items/{item_id}" ) async def update_item ( item_id: Annotated[int , Path(title="The ID of the item to get" , ge=0 , le=1000 )], q: str | None = None , item: Item | None = None , ): pass
路径操作将期望一个具有 Item 的属性的 JSON 请求体,就像:
1 2 3 4 5 6 { "name" : "Foo" , "description" : "The pretender" , "price" : 42.0 , "tax" : 3.2 }
多个请求体参数 1 2 3 4 @app.put("/items/{item_id}" ) async def update_item (item_id: int , item: Item, user: User ): results = {"item_id" : item_id, "item" : item, "user" : user} return results
在这种情况下,FastAPI 将注意到该函数中有多个请求体参数(两个 Pydantic 模型参数)。 因此,它将使用参数名称作为请求体中的键(字段名称),并期望一个类似于以下内容的请求体:
1 2 3 4 5 6 7 8 9 10 11 12 { "item" : { "name" : "Foo" , "description" : "The pretender" , "price" : 42.0 , "tax" : 3.2 } , "user" : { "username" : "dave" , "full_name" : "Dave Grohl" } }
请求体中的单一值 与使用 Query 和 Path 为查询参数和路径参数定义额外数据的方式相同,FastAPI 提供了一个同等的 Body。
例如,为了扩展先前的模型,你可能决定除了 item 和 user 之外,还想在同一请求体中具有另一个键 importance。
如果你就按原样声明它,因为它是一个单一值,FastAPI 将假定它是一个查询参数。
但是你可以使用 Body 指示 FastAPI 将其作为请求体的另一个键进行处理。
1 2 3 4 5 6 7 8 from fastapi import Body, FastAPI@app.put("/items/{item_id}" ) async def update_item ( item_id: int , item: Item, user: User, importance: Annotated[int , Body( )] ): results = {"item_id" : item_id, "item" : item, "user" : user, "importance" : importance} return results
在这种情况下,FastAPI 将期望像这样的请求体:
1 2 3 4 5 6 7 8 9 10 11 12 13 { "item" : { "name" : "Foo" , "description" : "The pretender" , "price" : 42.0 , "tax" : 3.2 } , "user" : { "username" : "dave" , "full_name" : "Dave Grohl" } , "importance" : 5 }
多个请求体参数和查询参数 当然,除了请求体参数外,你还可以在任何需要的时候声明额外的查询参数。 由于默认情况下单一值被解释为查询参数,因此你不必显式地添加 Query,你可以仅执行以下操作:q: str = None
嵌入单个请求体参数 假设你只有一个来自 Pydantic 模型 Item 的请求体参数 item。 默认情况下,FastAPI 将直接期望这样的请求体。 但是,如果你希望它期望一个拥有 item 键并在值中包含模型内容的 JSON,就像在声明额外的请求体参数时所做的那样,则可以使用一个特殊的 Body 参数 embed:item: Item = Body(embed=True)
1 2 3 4 @app.put("/items/{item_id}" ) async def update_item (item_id: int , item: Annotated[Item, Body(embed=True )] ): results = {"item_id" : item_id, "item" : item} return results
在这种情况下,FastAPI 将期望像这样的请求体:
1 2 3 4 5 6 7 8 { "item" : { "name" : "Foo" , "description" : "The pretender" , "price" : 42.0 , "tax" : 3.2 } }
请求体 - 字段 与使用 Query、Path 和 Body 在路径操作函数中声明额外的校验和元数据的方式相同,你可以使用 Pydantic 的 Field 在 Pydantic 模型内部声明校验和元数据。
Field 的工作方式和 Query、Path 和 Body 相同,包括它们的参数等等也完全相同。
1 2 3 4 5 6 7 8 9 from pydantic import BaseModel, Fieldclass Item (BaseModel ): name: str description: str | None = Field( default=None , title="The description of the item" , max_length=300 ) price: float = Field(gt=0 , description="The price must be greater than zero" ) tax: float | None = None
请求体 - 嵌套模型 使用 FastAPI,你可以定义、校验、记录文档并使用任意深度嵌套的模型(归功于Pydantic)。
List 字段 你可以将一个属性定义为拥有子元素的类型。例如 Python list:
1 2 3 4 5 6 class Item (BaseModel ): name: str description: str | None = None price: float tax: float | None = None tags: list [str ] = []
Set 字段
嵌套模型 我们可以定义一个 Image
模型: 然后我们可以将其用作一个属性的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Image (BaseModel ): url: str name: str class Item (BaseModel ): name: str description: str | None = None price: float tax: float | None = None tags: set [str ] = set () image: Image | None = None @app.put("/items/{item_id}" ) async def update_item (item_id: int , item: Item ): results = {"item_id" : item_id, "item" : item} return results
这意味着 FastAPI 将期望类似于以下内容的请求体:
1 2 3 4 5 6 7 8 9 10 11 { "name" : "Foo" , "description" : "The pretender" , "price" : 42.0 , "tax" : 3.2 , "tags" : [ "rock" , "metal" , "bar" ] , "image" : { "url" : "http://example.com/baz.jpg" , "name" : "The Foo live" } }
特殊的类型和校验 除了普通的单一值类型(如 str、int、float 等)外,你还可以使用从 str 继承的更复杂的单一值类型。 要了解所有的可用选项,请查看关于 来自 Pydantic 的外部类型 的文档 。
例如,在 Image 模型中我们有一个 url 字段,我们可以把它声明为 Pydantic 的 HttpUrl,而不是 str:
1 2 3 4 5 from pydantic import BaseModel, HttpUrlclass Image (BaseModel ): url: HttpUrl name: str
带有一组子模型的属性 你还可以将 Pydantic 模型用作 list、set 等的子类型:
1 2 3 4 5 6 7 class Item (BaseModel ): name: str description: str | None = None price: float tax: float | None = None tags: set [str ] = set () images: list [Image] | None = None
深度嵌套模型 你可以定义任意深度的嵌套模型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Image (BaseModel ): url: HttpUrl name: str class Item (BaseModel ): name: str description: str | None = None price: float tax: float | None = None tags: set [str ] = set () images: list [Image] | None = None class Offer (BaseModel ): name: str description: str | None = None price: float items: list [Item]@app.post("/offers/" ) async def create_offer (offer: Offer ): return offer
纯列表请求体 如果你期望的 JSON 请求体的最外层是一个 JSON array(即 Python list),则可以在路径操作函数的参数中声明此类型,就像声明 Pydantic 模型一样:
1 2 3 4 5 6 7 8 class Image (BaseModel ): url: HttpUrl name: str @app.post("/images/multiple/" ) async def create_multiple_images (images: list [Image] ): return images
任意 dict 构成的请求体 你也可以将请求体声明为使用某类型的键和其他类型值的 dict。 无需事先知道有效的字段/属性(在使用 Pydantic 模型的场景)名称是什么。
在下面的例子中,你将接受任意键为 int 类型并且值为 float 类型的 dict:
1 2 3 @app.post("/index-weights/" ) async def create_index_weights (weights: dict [int , float ] ): return weights
请记住 JSON 仅支持将 str 作为键。 但是 Pydantic 具有自动转换数据的功能。 这意味着,即使你的 API 客户端只能将字符串作为键发送,只要这些字符串内容仅包含整数,Pydantic 就会对其进行转换并校验。 然后你接收的名为 weights 的 dict 实际上将具有 int 类型的键和 float 类型的值。
模式的额外信息 - 例子 您可以在JSON模式中定义额外的信息。 一个常见的用例是添加一个将在文档中显示的example。 有几种方法可以声明额外的 JSON 模式信息。
其他数据类型 到目前为止,您一直在使用常见的数据类型,如:
但是您也可以使用更复杂的数据类型。
UUID:
一种标准的 “通用唯一标识符” ,在许多数据库和系统中用作ID。
在请求和响应中将以 str 表示。
datetime.datetime:
一个 Python datetime.datetime.
在请求和响应中将表示为 ISO 8601 格式的 str ,比如: 2008-09-15T15:53:00+05:00.
datetime.date:
Python datetime.date.
在请求和响应中将表示为 ISO 8601 格式的 str ,比如: 2008-09-15.
datetime.time:
一个 Python datetime.time.
在请求和响应中将表示为 ISO 8601 格式的 str ,比如: 14:23:55.003.
datetime.timedelta:
一个 Python datetime.timedelta.
在请求和响应中将表示为 float 代表总秒数。
Pydantic 也允许将其表示为 “ISO 8601 时间差异编码”, 查看文档了解更多信息。
frozenset:
在请求和响应中,作为 set 对待:
在请求中,列表将被读取,消除重复,并将其转换为一个 set。
在响应中 set 将被转换为 list 。
产生的模式将指定那些 set 的值是唯一的 (使用 JSON 模式的 uniqueItems)。
bytes: 标准的 Python bytes。
在请求和相应中被当作 str 处理。
生成的模式将指定这个 str 是 binary “格式”。
Decimal:
Cookie 参数 你可以像定义 Query 参数和 Path 参数一样来定义 Cookie 参数。
1 2 3 4 5 from fastapi import Cookie, FastAPI@app.get("/items/" ) async def read_items (ads_id: Annotated[str | None , Cookie( )] = None ): return {"ads_id" : ads_id}
你可以使用定义 Query, Path 和 Cookie 参数一样的方法定义 Header 参数。
1 2 3 4 5 from fastapi import FastAPI, Header@app.get("/items/" ) async def read_items (user_agent: Annotated[str | None , Header( )] = None ): return {"User-Agent" : user_agent}
自动转换 Header 在 Path, Query 和 Cookie 提供的功能之上有一点额外的功能。 大多数标准的headers用 “连字符” 分隔,也称为 “减号” (-)。 但是像 user-agent 这样的变量在Python中是无效的。
因此, 默认情况下, Header 将把参数名称的字符从下划线 (_) 转换为连字符 (-) 来提取并记录 headers. 同时,HTTP headers 是大小写不敏感的,因此,因此可以使用标准Python样式(也称为 “snake_case”)声明它们。
因此,您可以像通常在Python代码中那样使用 user_agent ,而不需要将首字母大写为 User_Agent 或类似的东西。 如果出于某些原因,你需要禁用下划线到连字符的自动转换,设置Header的参数 convert_underscores 为 False:
1 2 3 4 5 @app.get("/items/" ) async def read_items ( strange_header: Annotated[str | None , Header(convert_underscores=False )] = None ): return {"strange_header" : strange_header}
有可能收到重复的headers。这意味着,相同的header具有多个值。 您可以在类型声明中使用一个list来定义这些情况。 你可以通过一个Python list 的形式获得重复header的所有值。
比如, 为了声明一个 X-Token header 可以出现多次,你可以这样写:
1 2 3 @app.get("/items/" ) async def read_items (x_token: Annotated[list [str ] | None , Header( )] = None ): return {"X-Token values" : x_token}
响应 响应模型 你可以在任意的路径操作中使用 response_model 参数来声明用于响应的模型:
1 2 3 4 5 6 7 8 9 10 11 @app.post("/items/" , response_model=Item ) async def create_item (item: Item ) -> Any : return item@app.get("/items/" , response_model=list [Item] ) async def read_items () -> Any : return [ {"name" : "Portal Gun" , "price" : 42.0 }, {"name" : "Plumbus" , "price" : 32.0 }, ]
FastAPI 将使用此 response_model 来:
将输出数据转换为其声明的类型。
校验数据。
在 OpenAPI 的路径操作中为响应添加一个 JSON Schema。
并在自动生成文档系统中使用。
但最重要的是:
会将输出数据限制在该模型定义内。下面我们会看到这一点有多重要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class UserIn (BaseModel ): username: str password: str email: EmailStr full_name: str | None = None class UserOut (BaseModel ): username: str email: EmailStr full_name: str | None = None @app.post("/user/" , response_model=UserOut ) async def create_user (user: UserIn ) -> Any : return user
响应模型编码参数 你的响应模型可以具有默认值
1 2 3 4 5 6 7 8 9 10 11 class Item (BaseModel ): name: str description: Union [str , None ] = None price: float tax: float = 10.5 tags: List [str ] = []@app.get("/items/{item_id}" , response_model=Item, response_model_exclude_unset=True ) async def read_item (item_id: str ): return items[item_id]
response_model_exclude_unset=True
表明响应中将不会包含那些默认值,而是仅有实际设置的值。
你还可以使用:
response_model_exclude_defaults=True
response_model_exclude_none=True
response_model_include 和 response_model_exclude 你还可以使用路径操作装饰器的 response_model_include 和 response_model_exclude 参数。
它们接收一个由属性名称 str 组成的 set 来包含(忽略其他的)或者排除(包含其他的)这些属性。
1 2 3 4 5 @app.get( "/items/{item_id}/name" , response_model=Item, response_model_include={"name" , "description" }, )
额外的模型 多个模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from fastapi import FastAPIfrom pydantic import BaseModel, EmailStr app = FastAPI()class UserBase (BaseModel ): username: str email: EmailStr full_name: str | None = None class UserIn (UserBase ): password: str class UserOut (UserBase ): pass class UserInDB (UserBase ): hashed_password: str def fake_password_hasher (raw_password: str ): return "supersecret" + raw_passworddef fake_save_user (user_in: UserIn ): hashed_password = fake_password_hasher(user_in.password) user_in_db = UserInDB(**user_in.dict (), hashed_password=hashed_password) print ("User saved! ..not really" ) return user_in_db@app.post("/user/" , response_model=UserOut ) async def create_user (user_in: UserIn ): user_saved = fake_save_user(user_in) return user_saved
多个响应声明 你可以将一个响应声明为两种类型的 Union,
1 2 3 @app.get("/items/{item_id}" , response_model=Union [PlaneItem, CarItem] ) async def read_item (item_id: str ): return items[item_id]
模型列表 你可以用同样的方式声明由对象列表构成的响应。
1 2 3 @app.get("/items/" , response_model=list [Item] ) async def read_items (): return items
任意 dict 构成的响应 1 2 3 @app.get("/keyword-weights/" , response_model=dict [str , float ] ) async def read_keyword_weights (): return {"foo" : 2.3 , "bar" : 3.4 }
响应状态码 1 2 3 4 5 6 from fastapi import FastAPI, status@app.post("/items/" , status_code=status.HTTP_201_CREATED ) async def create_item (name: str ): return {"name" : name}
表单数据 接收的不是 JSON,而是表单字段时,要使用 Form。
1 2 3 4 5 from fastapi import FastAPI, Form@app.post("/login/" ) async def login (username: str = Form( ), password: str = Form( ) ): return {"username" : username}
请求文件
定义 File 参数
含 UploadFile 的文件参数
UploadFile 与 bytes 相比有更多优势:
使用 spooled 文件:
存储在内存的文件超出最大上限时,FastAPI 会把文件存入磁盘;
这种方式更适于处理图像、视频、二进制文件等大型文件,好处是不会占用所有内存;
可获取上传文件的元数据;
自带 file-like async 接口;
暴露的 Python SpooledTemporaryFile 对象,可直接传递给其他预期「file-like」对象的库。
1 2 3 4 5 6 7 8 @app.post("/files/" ) async def create_file (file: bytes = File( ) ): return {"file_size" : len (file)}@app.post("/uploadfile/" ) async def create_upload_file (file: UploadFile ): return {"filename" : file.filename}
处理错误 使用 HTTPException 1 2 3 4 5 6 7 from fastapi import FastAPI, HTTPException@app.get("/items/{item_id}" ) async def read_item (item_id: str ): if item_id not in items: raise HTTPException(status_code=404 , detail="Item not found" ) return {"item" : items[item_id]}
添加自定义响应头 1 2 3 4 5 6 7 8 9 @app.get("/items-header/{item_id}" ) async def read_item_header (item_id: str ): if item_id not in items: raise HTTPException( status_code=404 , detail="Item not found" , headers={"X-Error" : "There goes my error" }, ) return {"item" : items[item_id]}
安装自定义异常处理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from fastapi import FastAPI, Requestfrom fastapi.responses import JSONResponseclass UnicornException (Exception ): def __init__ (self, name: str ): self.name = name@app.exception_handler(UnicornException ) async def unicorn_exception_handler (request: Request, exc: UnicornException ): return JSONResponse( status_code=418 , content={"message" : f"Oops! {exc.name} did something. There goes a rainbow..." }, )@app.get("/unicorns/{name}" ) async def read_unicorn (name: str ): if name == "yolo" : raise UnicornException(name=name) return {"unicorn_name" : name}
覆盖默认异常处理器 FastAPI 自带了一些默认异常处理器。 触发 HTTPException 或请求无效数据时,这些处理器返回默认的 JSON 响应结果。 不过,也可以使用自定义处理器覆盖默认异常处理器
TODO ……