👏🏻 你好!欢迎访问「AI免费学习网」,0门教程,教程全部原创,计算机教程大全,全免费!

13 FastAPI 请求体与响应之请求体的处理

在上一篇文章中,我们探讨了如何使用路径参数来处理请求。在本篇文章中,我们将深入了解如何处理请求体,尤其是如何定义和解析 request body。请求体对于接收复杂数据(例如 JSON 文件、表单数据等)至关重要。我们将通过示例来说明如何在 FastAPI 中使用请求体。

什么是请求体?

请求体是 HTTP 请求中的一部分,通常用于向服务器发送数据。与路径参数和查询参数不同,请求体可以携带更复杂的数据结构,如 JSON 对象、XML 或表单字段。

在 FastAPI 中,我们通常使用 Pydantic 模型来定义请求体数据的结构,这不仅使得数据验证更加简单,而且提供了清晰的文档。

定义 Pydantic 模型

Pydantic 是一个数据验证和设置管理的库,它与 FastAPI 紧密集成。我们首先需要定义一个模型来描述我们的请求体。以下是一个示例模型:

1
2
3
4
5
6
from pydantic import BaseModel

class Item(BaseModel):
id: int
name: str
price: float

在上面的例子中,Item 模型包括三个字段:id(整数)、name(字符串)和 price(浮点数)。使用 Pydantic 后,FastAPI 可以为我们自动进行数据验证。

创建接收请求体的路由

接下来,我们将在 FastAPI 应用中创建一个路由,以接收该模型作为请求体。以下是实现此功能的代码示例:

1
2
3
4
5
6
7
8
from fastapi import FastAPI
from typing import List

app = FastAPI()

@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item

在此代码中,使用了 @app.post("/items/") 装饰器来定义一个新的 POST 路由。参数 item: Item 是该路由的请求体类型,FastAPI 会自动解析请求体并将其转换为 Item 类型。

测试请求体

我们可以使用 curl 或 Postman 等工具来测试此接口。以下是使用 curl 测试的示例命令:

1
curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d '{"id": 1, "name": "Apple", "price": 0.5}'

如果发送的请求体符合 Item 模型的定义,响应将返回相同的数据:

1
2
3
4
5
{
"id": 1,
"name": "Apple",
"price": 0.5
}

多个请求体参数

FastAPI 还支持接收多个请求体参数。可以通过List来处理多个相同类型的请求体。例如,如果我们希望一次创建多个 Item,可以做如下修改:

1
2
3
@app.post("/items/bulk", response_model=List[Item])
async def create_items(items: List[Item]):
return items

测试 bulk 路由的请求体可以如下进行:

1
curl -X POST "http://127.0.0.1:8000/items/bulk" -H "Content-Type: application/json" -d '[{"id": 1, "name": "Apple", "price": 0.5}, {"id": 2, "name": "Banana", "price": 0.3}]'

响应将返回传递的所有项:

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"id": 1,
"name": "Apple",
"price": 0.5
},
{
"id": 2,
"name": "Banana",
"price": 0.3
}
]

总结

在本篇文章中,我们详细探讨了如何在 FastAPI 中处理请求体,定义了 Pydantic 模型,并通过示例展示了如何接收和返回请求体的内容。这为后续文章提供了必要的基础,使我们能更好地理解如何处理响应。

在下一篇文章中,我们将探讨响应之响应的返回格式,包括如何设置响应模型、返回状态码等内容。希望您能继续关注这一系列教程!

分享转发

14 响应格式详解

在上一篇中,我们讨论了如何处理请求体和如何将请求体中的数据进行解析。在本篇中,我们将重点关注响应的格式,特别是如何在 FastAPI 中定义和返回响应体。最后,我们将通过一些案例来加深理解,为后续关于使用 JSON 返回数据的内容做好铺垫。

FastAPI中的响应体

在 FastAPI 中,响应体指的是服务器返回给客户端的数据格式。可以通过指定数据模型和内容类型来定义响应体。FastAPI 支持多种响应格式,如 JSON、HTML、Plain Text 等。

1. 使用响应模型

在 FastAPI 中定义响应体,我们通常会使用 Pydantic 模型。Pydantic 可以帮助我们定义数据结构并进行数据验证,这样可以确保返回的数据格式符合预期。

以下是一个简单的示例,演示如何使用 Pydantic 模型定义响应体:

1
2
3
4
5
6
7
8
9
10
11
12
13
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
id: int
name: str
price: float

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
return Item(id=item_id, name="Sample Item", price=9.99)

在上面的代码中,我们定义了一个 Item 类,表示我们要返回的响应体。路由 read_item 将返回一个 Item 类型的响应。当客户端访问 /items/{item_id} 时,FastAPI 将会自动将响应序列化为 JSON 格式。

2. 自定义响应格式

除了使用默认的 JSON 格式,我们还可以通过 FastAPI 提供的 Response 对象来自定义返回的响应格式。例如,我们可以设置状态码、内容类型等。

以下是一个示例,展示如何自定义响应:

1
2
3
4
5
6
7
8
9
from fastapi import FastAPI, Response
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/html", response_class=HTMLResponse)
async def get_html_response():
html_content = "<html><body><h1>Hello, World!</h1></body></html>"
return Response(content=html_content, media_type="text/html")

在这个示例中,我们使用 response_class 参数指定返回 HTMLResponse 格式,响应内容被定义为一种简单的 HTML 格式。当客户端访问 /html 路径时,返回一个简单的网页。

3. 处理错误响应

FastAPI 也支持返回错误响应。通过抛出 HTTPException,你可以为特定情况设定错误状态码和错误消息:

1
2
3
4
5
6
7
8
9
from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id < 0:
raise HTTPException(status_code=400, detail="Item ID must be positive")
return {"item_id": item_id}

在此示例中,如果请求中的 item_id 为负数,FastAPI 将返回一个 400 Bad Request 的 HTTP 响应,详细信息为 "Item ID must be positive"

结论

在本篇中,我们深入探讨了 FastAPI 中的响应体处理,包括如何使用 Pydantic 模型定义响应、如何自定义响应格式,以及如何处理错误响应。这些都是构建 API 时非常重要的部分,能够帮助我们确保返回的数据符合客户端的期望。

在下一篇文章中,我们将进一步探讨如何使用 JSON 格式返回数据,并补充更多实际代码示例,帮助大家更好地理解如何在项目中应用这些概念。

分享转发

15 生成请求体与响应之使用JSON返回数据

在上一篇文章中,我们讨论了FastAPI中请求体与响应的返回格式。这篇文章将进一步探讨如何使用JSON格式返回数据,包括如何定义请求体和响应的JSON结构。接下来,我们会结合案例,演示如何在FastAPI中做到这一点。

FastAPI中的JSON响应

FastAPI使用Pydantic模型来定义请求体和响应格式。通过这个功能,我们可以方便地生成和验证JSON数据,使我们的API更加安全和可靠。

定义Pydantic模型

首先,我们需要创建一个Pydantic模型来定义我们的请求和响应体。在本示例中,我们将设计一个简单的用户信息管理API。

1
2
3
4
5
6
7
8
9
10
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# 定义用户模型
class User(BaseModel):
id: int
name: str
email: str

在上面的代码中,我们定义了一个User类,继承自BaseModel。这个模型包含了我们希望在请求体和响应中传递的字段:idnameemail

创建一个接收JSON请求体的API

接下来,我们将创建一个API端点,允许用户通过POST请求发送JSON数据,并返回这些数据作为响应。

1
2
3
@app.post("/users/", response_model=User)
async def create_user(user: User):
return user

在这个示例中:

  • 我们使用@app.post装饰器来定义一个POST请求的路由/users/
  • 函数create_user接受一个类型为User的参数。这将自动进行数据验证。
  • 我们在response_model中指定响应的类型为User,这样FastAPI会自动将返回的数据转换为JSON格式。

测试API

我们可以使用curl或Postman来测试这个API。以下是一个使用curl的示例:

1
curl -X POST "http://127.0.0.1:8000/users/" -H "Content-Type: application/json" -d '{"id": 1, "name": "Alice", "email": "alice@example.com"}'

你应该会得到相应的JSON数据:

1
2
3
4
5
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}

生成复杂的JSON响应

FastAPI允许我们生成更复杂的JSON响应。假设我们想要返回用户的详细信息以及其创建的时间。我们可以更新模型以包含更多字段。

1
2
3
4
5
6
7
8
9
10
11
12
from datetime import datetime

class User(BaseModel):
id: int
name: str
email: str
created_at: datetime

@app.post("/users/", response_model=User)
async def create_user(user: User):
user.created_at = datetime.now() # 添加当前时间
return user

现在当我们发送一个创建用户的请求时,会得到包含created_at字段的响应。

小结

在本篇文章中,我们详细探讨了如何在FastAPI中生成请求体与响应的JSON数据。我们学习了如何:

  • 使用Pydantic模型定义请求体和响应结构。
  • 创建一个接收JSON请求的API,并返回相同的数据作为响应。
  • 扩展返回的JSON以包含更多信息。

下一篇文章我们将讨论路径参数与查询参数的使用示例,敬请期待!

分享转发

16 FastAPI路径参数的使用示例

在上一篇文章中,我们探讨了如何使用请求体与响应处理来返回JSON数据。在本篇文章中,我们将重点关注FastAPI中的路径参数,以及如何使用这些参数来构建动态的API接口。接下来,我们将为您展示一个完整的示例,帮助您更好地理解路径参数的使用方式。

什么是路径参数?

在RESTful API设计中,路径参数是指通过URL中的特定位置来传递的参数。这些参数通常用于标识资源的唯一性,比如用户ID、产品ID等。在FastAPI中,您可以通过在函数的参数中定义特定类型的路径参数来轻松实现。

基本示例:使用路径参数

我们将创建一个简单的示例,构建一个API,它可以返回特定用户的信息。首先,我们需要安装fastapiuvicorn(用于运行FastAPI应用):

1
pip install fastapi uvicorn

接下来,我们编写一个FastAPI应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from fastapi import FastAPI

app = FastAPI()

# 假设我们有一些用户数据
users = {
1: {"name": "Alice", "age": 30},
2: {"name": "Bob", "age": 25},
3: {"name": "Charlie", "age": 35},
}

# 使用路径参数来获取用户信息
@app.get("/users/{user_id}")
async def get_user(user_id: int):
user = users.get(user_id)
if user:
return user
return {"error": "User not found"}

在上面的代码中,我们做了以下事情:

  1. 创建API实例:使用FastAPI()创建一个API实例。
  2. 定义用户数据:创建一个简单的字典,模拟用户数据库。
  3. 定义路径参数:通过在路径/users/{user_id}中使用大括号,表示user_id是一个路径参数。
  4. 获取用户信息:在get_user函数中,根据传入的user_id返回相应的用户数据。如果用户不存在,则返回错误信息。

运行应用

我们可以使用以下命令运行上述FastAPI应用:

1
uvicorn main:app --reload

然后,我们可以在浏览器中访问 http://127.0.0.1:8000/users/1,您应该会看到如下的JSON响应:

1
2
3
4
{
"name": "Alice",
"age": 30
}

如果您访问一个不存在的用户,例如 http://127.0.0.1:8000/users/99,则会收到如下响应:

1
2
3
{
"error": "User not found"
}

路径参数的类型强制

在FastAPI中,路径参数的类型可以通过函数参数的类型注解确定。比如在上面的例子中,user_id: int确保传入的路径参数是整数类型。FastAPI会自动进行类型验证,如果传入的参数类型不匹配,则会返回406错误。

示例:多个路径参数

我们还可以使用多个路径参数来构建复杂的API。例如,让我们扩展一下这个应用,增加用户的地址信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 更新用户信息,添加地址数据
users = {
1: {"name": "Alice", "age": 30, "city": "New York"},
2: {"name": "Bob", "age": 25, "city": "Los Angeles"},
3: {"name": "Charlie", "age": 35, "city": "Chicago"},
}

@app.get("/users/{user_id}/address")
async def get_user_address(user_id: int):
user = users.get(user_id)
if user:
return {"city": user["city"]}
return {"error": "User not found"}

在这个例子中,我们增加了一个新的路径 /users/{user_id}/address,返回指定用户的城市信息。

请求示例

访问 http://127.0.0.1:8000/users/1/address,您会得到:

1
2
3
{
"city": "New York"
}

结论

在本篇文章中,我们深入探讨了在FastAPI中如何使用路径参数来创建动态的API接口。路径参数允许我们根据请求的URL动态获取资源,使得API更加灵活。在下一篇文章中,我们将讨论查询参数的使用,同样也是构建RESTful API不可或缺的一部分。

如果您对本教程有任何疑问或建议,欢迎在评论区留言。感谢您的阅读!

分享转发

17 FastAPI中的查询参数使用示例

在上一篇文章中,我们讨论了路径参数的使用,包括如何在FastAPI中定义和使用这些参数。今天,我们将重点关注查询参数的使用,以及如何在实际应用中将其与路径参数结合使用。

什么是查询参数?

查询参数是通过URL进行传递的额外数据,通常以键值对的形式出现在URL的问号?后面。例如,在以下URL中:

1
https://example.com/items?search=foo&limit=10

这里的searchlimit就是查询参数。

在FastAPI中,查询参数显式地定义为函数参数,并且可以设置默认值。让我们来看一些案例。

基本使用示例

下面是一个简单的FastAPI应用示例,演示了如何定义和使用查询参数。

1
2
3
4
5
6
7
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(search: str = None, limit: int = 10):
return {"search": search, "limit": limit}

在这个示例中,我们定义了一个GET请求的路由/items/,其中包含两个查询参数:

  • search: 一个可选的字符串参数,用于搜索项。
  • limit: 一个整数参数,表示返回的最大项目数量,默认为10。

访问查询参数

要访问这些查询参数,只需要在URL中添加相应的键值对。例如,发送以下请求:

1
GET /items/?search=python&limit=5

将返回:

1
2
3
4
{
"search": "python",
"limit": 5
}

如果不提供查询参数search,则返回的结果为:

1
2
3
4
{
"search": null,
"limit": 10
}

多个查询参数的组合

FastAPI允许组合使用多个查询参数,很容易构建复杂的查询请求。以下是一个示例,演示如何在同一个端点中使用多个查询参数。

1
2
3
@app.get("/products/")
async def read_products(category: str = None, max_price: float = None):
return {"category": category, "max_price": max_price}

可以通过如下URL请求:

1
GET /products/?category=electronics&max_price=300

返回:

1
2
3
4
{
"category": "electronics",
"max_price": 300
}

如果不提供categorymax_price,将得到:

1
2
3
4
{
"category": null,
"max_price": null
}

查詢參數的數據驗證

在FastAPI中,你可以对查询参数进行更复杂的验证。例如,你可以添加自定义验证,将limit限制在一定的范围内:

1
2
3
4
5
6
7
8
from fastapi import Query

@app.get("/items/")
async def read_items(
search: str = None,
limit: int = Query(default=10, le=100, ge=1) # limit必须在1到100之间
):
return {"search": search, "limit": limit}

此代码段中,我们使用了Query来定义limit的合法范围。如果请求的limit不在这个范围内,将返回422错误。

总结

在这篇文章中,我们介绍了FastAPI中的查询参数,包括如何定义、访问和验证这些参数。查询参数是构建灵活API的一部分,使得客户端能够更方便地与服务端交互。通过使用默认值和验证器,我们可以有效地控制输入,保证数据的有效性。

在下一篇文章中,我们将讨论路径参数与查询参数的参数类型的定义与验证,继续深入探讨FastAPI的强大特性。

分享转发

18 FastAPI路径参数与查询参数的类型定义与验证

在上一篇文章中,我们探讨了如何使用 FastAPI 创建查询参数,并通过实际示例来演示它们的用法。接下来,我们将深入了解如何定义路径参数和查询参数的参数类型,以及如何对这些参数进行验证。这一部分将为后续的数据验证与序列化的内容打下基础。

一、路径参数的定义与验证

路径参数是 URL 中的一部分,通常用于识别特定的资源。在 FastAPI 中,你可以在路由函数的参数中直接定义路径参数,FastAPI 会自动将 URL 路径中的相应部分解析为参数,并进行类型验证。

1.1 定义路径参数

以下是一个使用路径参数的基本示例:

1
2
3
4
5
6
7
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}

在上面的代码中,我们定义了一个名为 item_id 的路径参数。通过将其类型声明为 int,FastAPI 会自动验证传入的 item_id 是否为整数类型。如果请求的路径中 item_id 不是整数,FastAPI 将返回一个 422 Unprocessable Entity 错误。

1.2 类型验证

FastAPI 会根据参数的类型自动进行验证,例如:

  • 如果你访问 /items/42,结果将是 {"item_id": 42}
  • 如果你访问 /items/foo,则会返回一个错误,类似于:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "detail": [
    {
    "loc": ["path", "item_id"],
    "msg": "value is not a valid integer",
    "type": "value_error.integer"
    }
    ]
    }

我们可以定义其他类型的路径参数,例如 strfloat 等,也可以结合使用这些类型。

1.3 正则表达式参数

FastAPI 还支持路径参数的正则表达式。你可以使用 Path 来定义更复杂的路径参数。以下是一个示例,该示例限制了 item_name 参数只能包含字母和数字:

1
2
3
4
5
6
7
from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_name}")
async def read_item(item_name: str = Path(..., regex="^[a-zA-Z0-9]+$")):
return {"item_name": item_name}

在这个示例中,如果请求的路径名包含其他字符,例如 /items/item-1,将会返回 422 错误。

二、查询参数的定义与验证

查询参数则是路径后附加的参数,通常用于过滤、排序等功能。FastAPI 也支持对查询参数进行类型定义和验证。

2.1 定义查询参数

以下是一个使用查询参数的基本示例:

1
2
3
4
5
6
7
8
from fastapi import FastAPI
from typing import Optional

app = FastAPI()

@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}

在这个示例中,skiplimit 是查询参数,分别表示跳过的条数和返回的条数。它们都有默认值,如果不提供,它们将分别默认为 0 和 10。

2.2 使用 Optional 类型

你还可以使用 Optional 来标记一个查询参数为可选。例如:

1
2
3
@app.get("/items/")
async def read_items(skip: Optional[int] = 0, limit: Optional[int] = 10):
return {"skip": skip, "limit": limit}

如果请求路径中没有携带 skiplimit 参数,FastAPI 会自动将它们设为 None

2.3 合并路径参数与查询参数

接下来,我们可以将路径参数与查询参数结合使用。例如:

1
2
3
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Optional[str] = None):
return {"item_id": item_id, "query": q}

在这个示例中,我们定义了一个路径参数 item_id 和一个可选的查询参数 q。请求路径 /items/5?q=example 将返回 {"item_id": 5, "query": "example"}

三、总结

在本篇文章中,我们详细介绍了如何使用 FastAPI 定义和验证路径参数与查询参数。通过给路径参数和查询参数指定类型,FastAPI 能够进行自动验证,为 API 的使用提供了安全保障。接下来的文章,我们将进一步探讨如何使用 Pydantic 数据模型进行数据的验证与序列化,这将使我们的API更加健壮和易于使用。

通过这些基本的参数定义和验证机制,FastAPI 极大地提升了机构化和规范化 API 的能力,使得开发者能够专注于业务逻辑的实现而不是参数的处理。

分享转发

19 数据验证与序列化之数据模型定义

在上一篇文章中,我们讨论了路径参数与查询参数的定义与验证,此篇教程将继续围绕数据验证主题展开,重点讲解如何通过定义数据模型来实现有效的数据验证和序列化。这是一个重要的环节,有助于确保我们所接收和发送的数据都是符合预期的格式和类型。

数据模型的概念

在 FastAPI 中,数据模型通常是通过 Pydantic 提供的 BaseModel 类来定义的。Pydantic 是一个数据验证和设置管理的库,它不仅能够帮助我们定义数据结构,还能验证和序列化这些数据。

定义一个数据模型的基本步骤如下:

  1. **继承 BaseModel**:创建一个新的类,继承自 pydantic.BaseModel
  2. 定义属性:在新类中定义属性,并且为这些属性指定类型。
  3. 添加验证:可以使用 Pydantic 提供的多种字段类型和验证器来确保数据的有效性。

创建数据模型实例

接下来,让我们通过一个示例来演示如何定义数据模型。假设我们要处理用户信息,用户信息包含 idnameemail 等字段:

1
2
3
4
5
6
from pydantic import BaseModel, EmailStr, conint

class User(BaseModel):
id: conint(ge=1) # id 必须是大于等于 1 的整数
name: str
email: EmailStr # 确保 email 符合 email 格式

在这个例子中,我们使用了以下 Pydantic 特性:

  • conint(ge=1):定义一个整数类型,且要求其值大于或等于 1。
  • EmailStr:这个类型会自动验证字符串是否符合电子邮件格式。

使用数据模型进行验证与序列化

定义好数据模型后,我们可以在 FastAPI 的路径操作中使用它。以下是一个简单的 FastAPI 应用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from fastapi import FastAPI
from typing import List

app = FastAPI()

# 定义用户模型
class User(BaseModel):
id: conint(ge=1)
name: str
email: EmailStr

# 创建一个用户
@app.post("/users/", response_model=User)
async def create_user(user: User):
return user

在上述代码中,我们创建了一个 POST 路径 /users/,要求请求体中的数据符合 User 模型的定义。FastAPI 会自动处理输入数据的验证。如果输入数据不符合模型定义,FastAPI 会返回一个 422 Unprocessable Entity 错误。

示例请求

1
2
3
4
5
6
7
8
POST /users/
Content-Type: application/json

{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}

这个请求将成功返回:

1
2
3
4
5
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}

如果尝试提交一个无效的用户,例如缺少 email 字段或 id 是负数,FastAPI 会自动返回出错信息。

处理复杂数据模型

在实际应用中,我们的模型可能会更加复杂,如嵌套模型或列表等。以下是一个示例,展示了如何使用嵌套数据模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Address(BaseModel):
city: str
state: str
zip_code: str

class UserWithAddress(BaseModel):
id: conint(ge=1)
name: str
email: EmailStr
address: Address

@app.post("/users_with_address/", response_model=UserWithAddress)
async def create_user_with_address(user: UserWithAddress):
return user

在这个例子中,UserWithAddress 包含一个嵌套的 Address 模型。输入数据需要符合所有字段的 validation 规则。

示例请求

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /users_with_address/
Content-Type: application/json

{
"id": 2,
"name": "Jane Doe",
"email": "jane@example.com",
"address": {
"city": "New York",
"state": "NY",
"zip_code": "10001"
}
}

成功的返回将是:

1
2
3
4
5
6
7
8
9
10
{
"id": 2,
"name": "Jane Doe",
"email": "jane@example.com",
"address": {
"city": "New York",
"state": "NY",
"zip_code": "10001"
}
}

小结

在这一节中,我们学习了如何定义数据模型以实现数据验证与序列化,使用 Pydantic 创建了简单与复杂的模型,并通过 FastAPI 路径操作验证输入数据的有效性。这些技能将为后续的内容铺平道路,接下来我们将深入探讨如何使用 Pydantic 进行更复杂的数据验证。

接下来,我们将继续探索更高级的 Pydantic 功能,期待与您在下一篇文章中见面!

分享转发

20 数据验证与序列化之使用Pydantic进行数据验证

在上一篇文章中,我们介绍了如何使用 Pydantic 定义数据模型。此篇将深入探讨如何使用 Pydantic 进行数据验证与序列化。Pydantic 是 FastAPI 中的数据验证和解析的核心组件,它使得我们的工作变得更加简单和高效。

什么是数据验证?

数据验证是确保输入数据符合预期规范的过程。通过数据验证,我们可以确保数据的完整性和一致性,从而提高应用程序的可靠性。Pydantic 提供了一种便捷的方式来定义验证规则,并自动 validate 输入数据。

Pydantic 的基本用法

在 Pydantic 中,我们首先定义一个模型(model),然后可以使用该模型来验证输入的数据。以下是一个基本示例,展示了如何使用 Pydantic 模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pydantic import BaseModel, EmailStr, conint

class User(BaseModel):
name: str
email: EmailStr
age: conint(ge=18) # 年龄必须大于或等于 18 的整数

# 示例数据
user_data = {
"name": "Alice",
"email": "alice@example.com",
"age": 30
}

user = User(**user_data)
print(user)

在这个示例中,我们定义了一个 User 模型,它包含了 nameemailage 字段。Pydantic 会自动验证这些字段的类型和约束条件。

输入验证

当创建一个用户对象时,Pydantic 会检查提供的数据是否符合模型定义。如果数据无效,Pydantic 会抛出一个 ValidationError。例如:

1
2
3
4
5
6
7
8
9
10
invalid_user_data = {
"name": "Bob",
"email": "not-an-email",
"age": 15 # 这里应该抛出验证错误
}

try:
user = User(**invalid_user_data)
except ValidationError as e:
print(e.json())

在这个例子中,email 字段不符合 EmailStr 的格式,且 age 字段小于 18,Pydantic 将会返回详细的错误信息。

数据序列化

Pydantic 模型不仅可以用于验证,也可以轻松地将数据序列化为字典或 JSON 格式。使用 dict()json() 方法,可以将 Pydantic 对象转换为所需的格式:

1
2
3
4
5
user_json = user.json()
print(user_json) # 输出用户的 JSON 表示

user_dict = user.dict()
print(user_dict) # 输出用户的字典表示

定制序列化

Pydantic 还支持自定义字段序列化。您可以使用 Field 类来定义字段的别名或默认值。下面是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pydantic import Field

class User(BaseModel):
name: str
email: EmailStr
age: conint(ge=18)
is_active: bool = Field(default=True, alias='active_status')

user_data = {
"name": "Alice",
"email": "alice@example.com",
"age": 30
}

user = User(**user_data)
print(user.dict(by_alias=True)) # 使用别名序列化

在这个例子中,字段 is_active 被定义为 active_status 的别名。在转换为字典时,我们可以通过 by_alias=True 参数来使用别名进行序列化。

配合 FastAPI 使用

在 FastAPI 中,当定义一个接收 POST 请求的路由时,可以直接使用 Pydantic 模型作为请求体。FastAPI 将会自动处理数据验证和解析。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr, conint

app = FastAPI()

class User(BaseModel):
name: str
email: EmailStr
age: conint(ge=18)

@app.post("/users/")
async def create_user(user: User):
return user

这样,FastAPI 会自动验证请求数据,并将有效数据转换为 User 对象。对于无效数据,FastAPI 会直接返回 422 状态码及验证错误信息,而无需手动处理。

小结

本篇文章详细讲解了如何使用 Pydantic 进行数据验证与序列化,从基本的数据模型定义到错误处理、序列化以及在 FastAPI 中的使用。通过 Pydantic,我们可以高效地管理数据的完整性和一致性,减少了因为数据错误而导致的问题。

在下一篇文章中,我们将讨论如何实现自定义验证逻辑,进一步增强 Pydantic 模型的功能。敬请期待!

分享转发

21 自定义验证逻辑

在上一篇中,我们探讨了如何使用 Pydantic 进行数据验证与序列化。Pydantic 提供了强大的数据模型创建和验证功能,但在某些情况下,我们可能需要实现更复杂的验证逻辑。在这一节中,我们将介绍如何在 Pydantic 中定义自定义验证器,以实现符合我们业务逻辑的数据验证。

自定义验证器简介

Pydantic 中,我们可以通过定义自定义验证器来扩展基本的验证功能。这些验证器可以是类方法,也可以是实例方法,通常使用 @validator 装饰器来实现。

示例:基本自定义验证器

让我们首先来看一个简单的例子。假设我们有一个模型,要求用户提供一个年龄字段,年龄必须在 0 到 120 之间。我们可以通过自定义验证器来实现这一要求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pydantic import BaseModel, validator, ValidationError

class User(BaseModel):
name: str
age: int

@validator('age')
def validate_age(cls, v):
if not (0 <= v <= 120):
raise ValueError('Age must be between 0 and 120')
return v

# 测试自定义验证器
try:
user = User(name='Alice', age=25)
print(user)

user_invalid = User(name='Bob', age=130) # 触发验证错误
except ValidationError as e:
print(e)

在这个例子中,我们定义了一个 User 模型,并添加了一个名为 validate_age 的自定义验证器。该验证器接收字段值 v 作为参数,如果值不在有效范围内,则抛出 ValueError,这样 Pydantic 将会捕获这个错误并返回相应的验证错误信息。

多个字段之间的验证

有时候,我们需要验证多个字段之间的相互关系。例如,我们想要验证用户的出生年份是否合理,可以根据用户的年龄计算出一个出生年份,并进行检查。我们可以使用 @root_validator 对整个模型进行验证。

示例:多个字段验证

以下是一个关于验证出生年月是否合逻辑的示例:

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
from pydantic import BaseModel, validator, root_validator, ValidationError
from datetime import datetime

class User(BaseModel):
name: str
birth_year: int
age: int

@root_validator
def check_age_and_birth_year(cls, values):
birth_year = values.get('birth_year')
age = values.get('age')

current_year = datetime.now().year
expected_birth_year = current_year - age

if birth_year != expected_birth_year:
raise ValueError('The birth year and age do not match.')

return values

# 测试多个字段验证
try:
user = User(name='Alice', birth_year=1998, age=25) # 合法
print(user)

user_invalid = User(name='Bob', birth_year=1990, age=25) # 触发验证错误
except ValidationError as e:
print(e)

在这个例子中,我们使用 @root_validator 来检查 birth_yearage 这两个字段之间的关系。我们首先计算出根据年龄推算出的出生年份,然后与提供的 birth_year 字段进行对比。

处理复杂的数据结构

在处理复杂的数据结构时,可能需要对嵌套的模型进行自定义验证。Pydantic 支持嵌套模型,我们可以在父模型中对子模型的字段进行验证。

示例:嵌套模型验证

让我们看一个关于订单的例子,我们有一个 Order 模型,其中包含一个 User 模型:

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
from pydantic import BaseModel, validator, ValidationError

class User(BaseModel):
name: str
age: int

class Order(BaseModel):
user: User
total: float

@validator('total')
def validate_total(cls, v, values):
if v < 0:
raise ValueError('Total must be a positive value.')
if values.get('user').age < 18 and v > 1000:
raise ValueError('Users under 18 cannot make orders over 1000.')
return v

# 测试嵌套模型验证
try:
order = Order(user=User(name='Alice', age=19), total=500)
print(order)

order_invalid = Order(user=User(name='Bob', age=17), total=1500) # 触发验证错误
except ValidationError as e:
print(e)

在这个例子中,我们在 Order 模型中验证了订单的 total 字段,同时考虑了用户的年龄。若用户年龄小于 18 岁,则其订单总额不能超过 1000。

总结

自定义验证器在 Pydantic 中是一个非常强大的功能,允许我们定义复杂的验证逻辑以满足具体的业务需求。在本节中,我们探讨了如何使用 @validator@root_validator 实现单字段和多字段的验证,此外还讨论了如何对嵌套模型进行验证。

在下一篇中,我们将讨论错误处理的常见策略,以及如何优雅地处理和返回验证错误。这样一来,我们就能够为用户提供更好的反馈体验。希望大家在实际开发中能够灵活运用 Pydantic 的验证特性,编写出质量更高的代码!

分享转发

22 快速掌握FastAPI中的常见错误处理

在上一篇中,我们讨论了如何在FastAPI中实现自定义验证逻辑,为构建灵活且健壮的API提供了基础。在本篇,我们将重点关注错误处理,特别是如何处理在API运行时可能遇到的常见错误。这部分的知识将有助于你更好地管理应用程序中的异常,使得API在发生错误时能够返回友好的提示信息,而不是直接崩溃。

常见错误处理

在FastAPI中,我们可以使用两个主要的机制来处理错误:HTTPException依赖项异常处理。这两种方法都可以帮助我们返回合适的HTTP状态码和错误消息。

1. 使用HTTPException

HTTPException是FastAPI提供的一个异常类,便于我们在API函数中显式引发HTTP错误。例如,当用户请求了一个不存在的资源时,我们可以返回404状态码。

示例:资源未找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from fastapi import FastAPI, HTTPException

app = FastAPI()

# 模拟的数据库
fake_items_db = {
1: {"name": "Item 1"},
2: {"name": "Item 2"},
}

@app.get("/items/{item_id}")
async def read_item(item_id: int):
item = fake_items_db.get(item_id)
if item is None:
raise HTTPException(status_code=404, detail="Item not found")
return item

在这个例子中,我们定义了一个API端点/items/{item_id},如果用户请求的item_idfake_items_db中不存在,我们就会引发HTTPException,返回404状态和"Item not found"的详细信息。

2. 处理验证错误

当我们使用Pydantic模型时,输入数据的验证可以自动处理,并且在验证失败时会返回422状态码。我们可以自定义验证错误的响应,以便提供更清晰的信息。

示例:自定义Pydantic模型错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field, ValidationError

app = FastAPI()

class Item(BaseModel):
id: int
name: str = Field(..., min_length=1)

@app.post("/items/")
async def create_item(item: Item):
return item

@app.exception_handler(ValidationError)
async def validation_exception_handler(request, exc):
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "body": exc.body},
)

在这个例子里,当请求的body数据不符合Item模型的结构时,我们使用@app.exception_handler(ValidationError)装饰器来自定义验证错误的处理。这样能够让错误信息更加清晰且结构化。

3. 捕获未处理异常

有时我们可能会遇到未被捕获的通用异常。在FastAPI中,我们可以使用全局错误处理器来处理所有未处理的异常。

示例:全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/divide/{a}/{b}")
async def divide(a: int, b: int):
return a / b

@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
return JSONResponse(
status_code=500,
content={"detail": "An error occurred. Please try again later."},
)

在这个例子中,我们定义了一个简单的除法API。若用户输入0作为分母,这将引发ZeroDivisionError。我们通过全局异常处理器global_exception_handler捕获所有未处理的异常,返回500状态和一个通用的错误信息。

小结

在本篇文章中,我们讨论了如何在FastAPI应用中处理常见的错误。我们学习了通过HTTPException返回特定的HTTP错误状态,以及如何自定义输入验证错误和全局异常处理。这些技巧将帮助我们构建出对用户更友好的API,提高应用的稳定性。

在下一篇中,我们将讨论自定义异常处理,为API添加更多层次的错误管理,使其更加健壮。请继续关注!

分享转发

23 FastAPI 自定义异常处理

在上一篇中,我们讨论了 FastAPI 中的常见错误处理。了解了如何使用内置的异常和中间件来捕获常见错误后,接下来,我们将深入了解如何创建自定义异常处理器,以便对特定错误进行处理并返回自定义的响应。

自定义异常

首先,我们需要定义自定义异常。通过定义异常类,我们可以在 FastAPI 应用程序中捕获特定类型的错误。例如,让我们定义一个简单的 ItemNotFound 异常:

1
2
3
class ItemNotFound(Exception):
def __init__(self, item_id: int):
self.item_id = item_id

这个自定义异常类接受一个 item_id 参数,用于指明未找到的项目。

创建自定义异常处理器

接下来,我们需要为我们的自定义异常类实现一个异常处理器。FastAPI 提供了一个装饰器 app.exception_handler(),我们可以利用它来注册自定义的异常处理函数。

1
2
3
4
5
6
7
8
9
10
11
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(ItemNotFound)
async def item_not_found_exception_handler(request: Request, exc: ItemNotFound):
return JSONResponse(
status_code=404,
content={"message": f"Item with id {exc.item_id} not found."},
)

在上面的代码中,item_not_found_exception_handler 函数接收一个 Request 和我们的自定义异常 ItemNotFound 作为参数。我们利用 JSONResponse 返回了一个 JSON 格式的错误消息,包含了状态码和详细信息。

在路由中抛出自定义异常

现在,我们可以在路由中使用这个自定义异常。假设我们有一个简单的项目管理 API,用户可以通过 ID 获取项目,但如果项目不存在则抛出 ItemNotFound 异常:

1
2
3
4
5
6
7
8
9
10
from fastapi import Path

# 示例数据
items = {1: "item 1", 2: "item 2"}

@app.get("/items/{item_id}")
async def read_item(item_id: int = Path(..., description="The ID of the item to retrieve")):
if item_id not in items:
raise ItemNotFound(item_id=item_id)
return {"item": items[item_id]}

在这个路由中,我们检查请求的 item_id 是否在示例数据 items 中。如果没有找到对应的项,我们就抛出 ItemNotFound 异常,从而触发我们之前定义的自定义异常处理器。

测试自定义异常处理

现在让我们测试这个例子。启动 FastAPI 应用程序,并尝试访问:

  • 有效的 ID:GET /items/1 将返回 { "item": "item 1" }
  • 无效的 ID:GET /items/3 将返回一个自定义错误响应:
1
2
3
{
"message": "Item with id 3 not found."
}

小结

通过本节的学习,我们创建了一个自定义异常类 ItemNotFound,并实现了一个专门的异常处理器来返回自定义的错误响应。这种方法在处理复杂应用中的特定错误时非常有用,它让我们的 API 返回更加友好的错误信息。

接下来,在下一篇中,我们将讨论 FastAPI 中的 HTTP 异常响应,以便更好地处理和返回各种 HTTP 状态码的错误。这将与我们自定义异常处理的内容形成良好的补充。

分享转发

24 FastAPI错误处理之HTTP异常响应

在上一篇中,我们讨论了如何在FastAPI中实现自定义异常处理。在这一篇中,我们将继续错误处理的主题,重点介绍如何返回HTTP异常响应,以及如何利用FastAPI内置的异常处理机制,提高应用程序的健壮性。

1. HTTP异常响应的介绍

在FastAPI中,HTTP异常响应用于指示请求中发生的错误,并返回合适的HTTP状态码和错误信息。通常情况下,我们会利用HTTPException类来处理常见的错误情况,例如请求的资源未找到、权限不足等。

1.1 使用HTTPException

HTTPException是FastAPI内置的异常类,它允许我们抛出HTTP错误。在发生异常时,FastAPI会自动异常捕获,并返回相应的HTTP响应。

1
2
3
4
5
6
7
8
9
10
11
12
from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id < 0:
raise HTTPException(status_code=400, detail="Item ID must be a positive integer")
if item_id > 10:
raise HTTPException(status_code=404, detail="Item not found")

return {"item_id": item_id, "name": f"Item {item_id}"}

在这个例子中,我们定义了一个获取商品信息的接口/items/{item_id}。如果item_id小于0,则抛出400状态码的异常,说明请求参数不合法;如果item_id大于10,则抛出404状态码的异常,表示没有找到对应的商品。

2. 自定义错误响应

虽然可以使用HTTPException生成简单的错误响应,但有时我们需要提供更详细的错误信息。这时候,可以通过继承HTTPException,来自定义我们的错误响应结构。

2.1 自定义异常类

创建一个自定义异常类时,我们可以添加更多上下文信息,来帮助用户理解错误的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

class CustomHTTPException(HTTPException):
def __init__(self, status_code: int, detail: str, errors: list = None):
super().__init__(status_code=status_code, detail=detail)
self.errors = errors or []

app = FastAPI()

@app.post("/items/")
async def create_item(item: BaseModel):
if "name" not in item.dict():
raise CustomHTTPException(
status_code=422,
detail="Validation Error",
errors=["'name' is required"]
)
return {"item": item}

在这个例子中,我们定义了一个CustomHTTPException,它允许我们传递额外的错误信息。在create_item接口中,如果请求的数据不包含name字段,则返回422状态码及相应的错误信息。

3. 快速错误响应的统一处理

为了避免在每个路由处理函数中手动抛出异常,我们可以创建一个全局错误处理器。这样可以集中管理错误响应,提高代码的可维护性。

3.1 设置全局异常处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from fastapi import FastAPI, Request

app = FastAPI()

@app.exception_handler(CustomHTTPException)
async def custom_http_exception_handler(request: Request, exc: CustomHTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"message": exc.detail, "errors": exc.errors},
)

@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id < 0:
raise CustomHTTPException(status_code=400, detail="Item ID must be a positive integer")
if item_id > 10:
raise CustomHTTPException(status_code=404, detail="Item not found")

return {"item_id": item_id, "name": f"Item {item_id}"}

在这个示例中,我们使用@app.exception_handler()装饰器注册了一个全局HTTP异常处理器。所有抛出的CustomHTTPException都会被该处理器捕获,返回统一格式的JSON响应。

4. 总结

在本文中,我们介绍了FastAPI中如何使用HTTPException来处理HTTP异常响应,并探讨了如何通过自定义异常类及全局异常处理器来改善错误应对机制。这使得错误处理更加灵活和可控,为API的使用者提供了更友好的错误信息。

在下一篇,我们将深入探讨中间件与依赖注入的概念与使用,帮助你构建更复杂的FastAPI应用。希望本篇能为你在项目中的错误处理提供参考和帮助!

分享转发