FastAPI 快速开发 Web API 项目: 响应模型与错误处理
FastAPI 快速开发 Web API 项目学习笔记:
- 第一篇:通过 Python FastAPI 开发一个快速的 Web API 项目
- 第二篇:FastAPI 的路由介绍与使用
- 第三篇:FastAPI 开发中数据校验利器 Pydantic 介绍与集成使用
- 第四篇:FastAPI 快速开发 Web API 项目: 路径参数和查询参数
- 第五篇:从 Flask 转换到 FastAPI(翻译)
介绍
在前面的文章中,我们可以通过注释路径操作函数返回类型来声明用于响应的类型。
因此,我们可以像在函数参数中输入数据一样使用类型注释,使用 Pydantic 模型、列表、字典、标量值(如整数、布尔值等)。
响应模型
响应模型作为模板,用于从 API 路由的路径返回数据。它们建立在 Pydantic 上,以正确解析从客户端到服务器的请求响应。
Responses 是 API 生命周期的一个组成部分。响应是通过任何标准的 HTTP 方法与 API 路由交互而收到的反馈。一个 API 响应通常是 JSON 或 XML 格式,但它也可以是文档形式。响应一般由一个头和一个响应体组成。
响应头
响应头由请求的状态和附加信息组成,以指导响应体的交付。响应头中包含的信息的一个例子是 头中包含的信息的一个例子是 Content-Type,它告诉客户端返回的内容类型。
响应体
另一方面,响应体是客户端向服务器请求的数据。
响应体是由 Content-Type 头变量决定的,最常用的是 application/json 。
建立 FastAPI 响应模型
FastAPI 可以为 response_model
定义 API 输出的数据格式:
- 对 API 响应返回的数据进行验证
- 转化输出数据对应 response_model 所定义的数据模型,它将限制和过滤输出数据,使之符合返回类型中的定义。这对于 API 安全性尤为重要
- 可以根据 response_model 指定的数据格式,限制可以输出的数据:如果数据是无效的(例如,你缺少一个字段),这意味着你的应用程序代码被破坏了,没有返回它应该返回的东西,它将返回一个服务器错误,而不是返回错误的数据
- 为响应添加一个 JSON schema,在 OpenAPI 的路由操作中上体现,方便开发者可以直接从文件上看到 API 需要的文件格式,它也将被自动客户端代码生成工具所使用
之前的文章中学习了如何使用 Pydantic 来构建模型。在 FastAPI 中,响应模型也是建立在 Pydantic 上的,但其目的不同。例如,在路由路径的定义中,我们有以下内容:
@todo_router.get("/todo")
async def retrieve_todos() -> dict:
return {
"todos": todo_list
}
该路由返回数据库中存在的待办事项的列表。下面是一些输出的例子:
{
"todos": [
{
"id": 1,
"name": "write a todo",
"item": "write a todo project"
},
{
"id": 2,
"name": "learning path",
"item": "learning path parameters"
},
{
"id": 3,
"name": "learning query",
"item": "learning query parameters"
}
]
}
该路由返回存储在 todos
数组中的所有内容。为了指定要返回的信息,我们必须把要显示的数据分开,或者引入额外的逻辑。幸运的是,我们可以创建一个包含我们想要返回的字段的模型,并使用 response_model 参数将其添加到我们的路由定义中。
先定义一个新的模型类来返回待办事项的列表,在 model.py
中进行代码变更:
from pydantic import BaseModel, StrictInt
from typing import List
class Todo(BaseModel):
id: StrictInt
name: str
item: str
class TodoItem(BaseModel):
item: str
class Config:
schema_extra = {
"example": {
"item": "test"
}
}
class TodoItems(BaseModel):
todos: List[TodoItem]
class Config:
schema_extra = {
"example": {
"todos": [
{
"item": "Example schema 1!"
},
{
"item": "Example schema 2!"
}
]
}
}
$ curl -X 'POST' 'http://127.0.0.1:8888/todo' -H 'accept: application/json' -H 'Content-Type: application/json' -d '{"id": 1,"name": "test", "item": "This todo will be retrieved without exposing my ID!"}'
{"message":"Todo added successfully"}
$ curl -X 'GET' 'http://127.0.0.1:8888/todo' -H 'accept: applicatiion/json'
{"todos":[{"item":"This todo will be retrieved without exposing my ID!"}]}
状态码
状态码是服务器为响应客户的请求而发出的独特的短代码。 在 HTTP 中,您发送一个 3 位数的数字状态代码作为响应的一部分。这些状态代码有一个关联的名称来识别它们,但重要的部分是数字。
响应状态代码被分为五类,每一类表示不同的响应:
- 100 及以上为“信息”。你很少直接使用它们。具有这些状态代码的响应不能有正文。
- 200 及以上表示“成功”响应。这些是您最常使用的。
- 200 是默认状态代码,表示一切正常。
- 另一个例子是 201,“已创建”。它通常在数据库中创建新记录后使用。
- 一个特例是 204,“无内容”。当没有内容返回给客户端时使用此响应,因此响应不能有正文。
- 300 及以上用于“重定向”。具有这些状态代码的响应可能有也可能没有正文,但 304“未修改”除外,它不能有正文。
- 400 及以上用于“客户端错误”响应。这些是您可能最常使用的第二种类型。
- 一个示例是 404,表示“未找到”响应。
- 对于来自客户端的一般错误,您可以只使用 400。
- 500 及以上是服务器错误。你几乎从不直接使用它们。当您的应用程序代码或服务器的某个部分出现问题时,它会自动返回这些状态代码之一。
总结就是:
- 1XX: 请求被接收
- 2XX: 请求成功
- 3XX: 请求重定向
- 4XX: 客户端错误
- 5XX: 服务端错误
完整的状态码列表可以点此处。
在构建 Web 应用程序时,不管是什么框架,所遵循的标准做法是为各个事件返回适当的状态代码。400 状态码不应该被返回给服务器错误。同样地,200 状态码也不应该在请求操作失败时返回。
定义HTTP状态码
除了定义 response_model
之外,也可以在路由操作中确定 API 预期返回的 HTTP 状态码:
@app.post( '/user/login' , response_model=UserBase, status_code= 201 )
def login ( user_in: UserIn ):返回user_in
如果 HTTP 状态码记不过来,但是知道状态消息类别,也可以使用 FastAPI 提供的状态:
from fastapi import FastAPI, status
@app.post( '/user/login' , response_model=UserBase, status_code=status.HTTP_201_CREATED )
def login ( user_in: UserIn ):返回user_in
FastAPI 将使用该时间响应来提取状态代码(还有 cookie 和标头),并将它们放入包含您返回的值的最终响应中,由任何 response_model 过滤。
您也可以在依赖项中声明 Response 参数,并在其中设置状态码。但请记住,最后设置的将获胜。
错误处理
错误处理包括在处理应用程序的错误时涉及的实践和活动。这些做法包括返回适当的错误状态代码和错误信息。
请求可能会返回错误的响应,这些响应可能是意料之外的,或者对请求失败原因的信息不足。请求的错误可能是由于试图访问不存在的资源、没有足够权限的受保护页面,甚至是服务器错误。FastAPI 中的错误是通过使用 FastAPI 的 HTTPException 类引发一个异常来处理的:
- 状态码:返回失败的状态码
- 描述:将向客户发送的附带信息
- 头:一个可选的参数,用于需要头信息的响应
在我们的待办事项路由定义中,当一个待办事项找不到时,我们会返回一个消息。我们将更新它以引发 HTTPException 。HTTPException 允许我们返回一个适当的错误响应代码。
如果根据上一节路由中的方法,如果访问一个 id
不存在的待办事项: 1111,我们虽然会返回一个错误信息,但是状态码是返回 200,这与状态码的规则是不相符的,如图所示:
HTTP/1.1 200 OK
content-length: 50
content-type: application/json
date: Mon, 10 Apr 2023 06:42:15 GMT
server: uvicorn
{"message":"Todo with supplied ID doesn't exist."}
因此,我们需要修改一下代码,使用 HTTPException,如下:
@todo_router.get("/todo/{todo_id}")
async def get_single_todo(todo_id: int = Path(...,
title="The ID of the todo to be retrieved.")) -> dict:
for todo in todo_list:
# if todo["id"] == todo_id:
if todo.id == todo_id:
return {
"todo": todo
}
# return {
# "message": "Todo with supplied ID doesn't exist."
# }
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail= "Todo with supplied ID doesn't exist",
)
此时,我们返回的状态码就是 404 Not Found,
总结
本文学习了什么是响应和响应模型,以及错误处理的含义,并了解了 HTTP 状态码以及它的含义。并利用 FastAPI 创建了一个响应模型,只返回待办事项列表中的 item ,而不返回其 id。最后,我们学习了错误和错误处理,让 Todo 应用在发生查不到的情况返回 404 错误,而不是 200 状态码。
希望本文能对你有所帮助,如果喜欢本文,可以点个关注.
下一篇文章见!宇宙古今无有穷期,一生不过须臾,当思奋争。
参考链接:
- 点赞
- 收藏
- 关注作者
评论(0)