fastapi教程(四):做出响应

请求体现的是后端的数据服务能力,而响应体现的是后端向前端的数据展示能力。

一,一个完整的web响应应该包含哪些东西

一个完整的 Web 响应通常包含以下几个主要部分:

1. 状态行- HTTP 版本- 状态码- 状态消息例如:`HTTP/1.1 200 OK`2. 响应头
常见的响应头包括:a. 通用头部:- Date: 响应生成的日期和时间- Connection: 连接状态(如 keep-alive 或 close)b. 响应特定头部:- Server: 服务器软件名称和版本- Content-Type: 响应体的 MIME 类型- Content-Length: 响应体的长度(以字节为单位)- Content-Encoding: 响应体的编码方式(如 gzip)c. 实体头部:- Last-Modified: 资源的最后修改日期- ETag: 资源的唯一标识符- Expires: 资源的过期时间- Cache-Control: 缓存控制指令d. 安全相关头部:- Set-Cookie: 设置 HTTP cookie- X-XSS-Protection: 控制浏览器的 XSS 筛选器- X-Frame-Options: 控制页面是否可以被嵌入框架- Content-Security-Policy: 内容安全策略- Strict-Transport-Security: 强制使用 HTTPSe. 跨域相关头部:- Access-Control-Allow-Origin: 指定允许跨域请求的源- Access-Control-Allow-Methods: 允许的 HTTP 方法- Access-Control-Allow-Headers: 允许的请求头3. 空行用于分隔头部和响应体4. 响应体- 包含请求的资源或处理结果- 格式取决于 Content-Type(如 HTML、JSON、XML、图片等)

示例:

HTTP/1.1 200 OK
Date: Mon, 23 May 2023 12:28:53 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: application/json; charset=utf-8
Content-Length: 234
Cache-Control: max-age=3600
ETag: "686897696a7c876b7e"
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains{"id": 12345,"name": "Example Product","description": "This is an example product description.","price": 99.99,"inStock": true
}

注意事项:

  1. 并非所有响应都需要包含响应体(如 204 No Content)。
  2. 响应头的具体内容会根据请求和应用需求而变化。
  3. 在实际应用中,应当根据安全需求和性能考虑来选择合适的响应头。
  4. 对于流式响应或大文件传输,可能会使用分块传输编码(Transfer-Encoding: chunked)。
  5. 在设计 API 时,应考虑响应的一致性,包括错误处理和状态码的使用。

二,响应

(一)简单响应

就像前面我们定义的路由处理函数一样,可以通过返回一个字典来返回一个简单的响应内容:

import uvicorn
from fastapi import FastAPI, Headerapp = FastAPI()@app.get("/test/")
def test():response = {"message": "Hello, World!"}print(type(response))   # -> <class 'dict'>return responseif '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

通过打印的内容我们可以看到,返回的是一个 Python 字典,来看看 FastAPI 是怎么处理它的:
在这里插入图片描述

具体到不同的客户端情况:
1,使用浏览器直接访问:

  • 你会看到 JSON 格式的文本

2,使用 JavaScript/Ajax:

fetch('http://127.0.0.1:8088/test/').then(response => response.json()).then(data => console.log(typeof data, data));
// 输出:object {message: "Hello, World!"}
  • 接收到的是 JavaScript 对象

3,使用 Python requests 库:

import requests
response = requests.get('http://127.0.0.1:8088/test/')
data = response.json()
print(type(data), data)
# 输出:<class 'dict'> {'message': 'Hello, World!'}
  • 解析后得到的是 Python 字典

4,使用 curl 命令行工具:

curl http://127.0.0.1:8088/test/
# 输出:{"message": "Hello, World!"}
  • 接收到的是 JSON 格式的字符串

(二)响应数据模型

我们可以定义请求体的数据模型来接收请求体的数据,同样也可以定义响应数据模型来规范响应数据。

  • 响应数据模型在 API 文档页中为 JSON 格式。

只需要在任意的路由处理函数中使用 response_model 参数来声明用于响应的模型。
FastAPI 将使用此 response_model 来:

  • 将输出数据转换为其声明的类型。
  • 校验数据。
  • 在 OpenAPI 的路径操作中为响应添加一个 JSON Schema。
  • 并在自动生成文档系统中使用。

1,定义与使用响应数据模型

首先同样使用 pydantic 定义响应数据模型:

import uvicornfrom typing import Optional
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()# 1,定义请求提数据模型
class User(BaseModel):first_name: strlast_name: str# 2,定义响应数据模型
class UserExtra(BaseModel):first_name: strlast_name: strfull_name: str | None = None# 处理请求
@app.post("/create-user/", response_model=UserExtra)
async def create_user(user: User) -> Any:# 4,处理请求数据full_name = user.first_name + ' ' + user.last_name# 5,返回数据return {"first_name": user.first_name,"last_name": user.last_name,"full_name": full_name}if '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

之所以要将响应模型放在参数中声明,而不是放在函数返回值中使用,是因为路由处理函数可能不会真正返回响应模型(可能是一个 dict、数据库对象或其他模型),这是就可以使用 response_model 来执行字段约束和序列化。

查看 API:
在这里插入图片描述
在这里插入图片描述
当路由处理函数的返回值无法被 response_model 处理成满足响应模型的数据的时候,就会报错:

import uvicornfrom typing import Optional
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()# 1,定义请求提数据模型
class User(BaseModel):first_name: strlast_name: str# 2,定义响应数据模型
class UserExtra(BaseModel):first_name: strlast_name: strfull_name: str | None = None# 处理请求
@app.post("/create-user/", response_model=UserExtra)
async def create_user(user: User):# 4,处理请求数据full_name = user.first_name + ' ' + user.last_name# 5,返回数据return {"full_name": full_name}if '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

在这里插入图片描述
在这里插入图片描述
当路由处理函数的返回值“太丰富”时,response_model 还会自动过滤掉无关内容:

import uvicornfrom typing import Optional
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()# 1,定义请求提数据模型
class User(BaseModel):first_name: strlast_name: str# 2,定义响应数据模型
class UserExtra(BaseModel):first_name: strlast_name: strfull_name: str | None = None# 处理请求
@app.post("/create-user/", response_model=UserExtra)
async def create_user(user: User):# 4,处理请求数据full_name = user.first_name + ' ' + user.last_name# 5,返回数据return {"first_name": user.first_name,"last_name": user.last_name,"full_name": full_name,"extra": "extra"}if '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

在这里插入图片描述

2,对响应数据模型的控制

如果我们在定义响应数据模型时使用了默认值,通常情况下,会在请求响应中补上这些字段并使用其默认值。

如果组要排除这些字段的出现,可以使用 response_model_exclude_unset 参数:

from typing import Any
import uvicornfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()# 1,响应数据模型
class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = 1111tags: list[str] = ['tag1', 'tag2']@app.get("/items/", response_model=list[Item], response_model_exclude_unset=True)
async def read_items() -> Any:return [{"name": "Portal Gun", "price": 42.0},{"name": "Plumbus", "price": 32.0, "tax": 3.2},]if __name__ == '__main__':uvicorn.run(app)

这些控制字段来自 pydantic,还有:

  • response_model_exclude_unset=True:排除未设置的字段;
  • response_model_exclude_defaults=True:排除默认值的字段;
  • response_model_exclude_none=True:排除值为 None 的字段;
  • response_model_exclude={}:排除指定的字段,{}是一个set
  • response_model_include={}:包含指定的字段,{}是一个set

fastapi 官方更建议使用多个响应数据模型而不是疯狂使用上面这些个参数:因为即使使用 response_model_includeresponse_model_exclude 来省略某些属性,在应用程序的 OpenAPI 定义(和文档)中生成的 JSON Schema 仍将是完整的模型。

3,业务数据模型

在实际场景中,获得请求数据后会经过一系列业务处理后才能生成响应数据,这个过程中往往还需要使用到其他数据模型。

例如,在处理用户注册业务的路由处理函数中:

  • 接受请求数据的请求数据模型包含用户名和密码
  • 业务模型包含用户名、密码的哈希值
  • 响应数据模型包含用户名

通常来说,我们应该将各个数据模型独立出来,形成请求数据模型、业务数据模型和响应数据模型。

from datetime import datetimeimport uvicornfrom typing import Any, Optional
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class UserIn(BaseModel):"""请求数据模型"""username: str  # 必填password: str  # 必填# 以下字段非必填email: Optional[str] = Nonephone: Optional[str] = Noneaddress: Optional[str] = Noneclass UserDb(BaseModel):"""数据库业务模型"""username: strpassword_hash: stremail: Optional[str] = Nonephone: Optional[str] = Noneaddress: Optional[str] = Noneupdate_time: datetime = datetime.now()class UserOut(BaseModel):"""响应数据模型"""username: stremail: Optional[str] = Nonephone: Optional[str] = Noneaddress: Optional[str] = Noneupdate_time: datetimedef get_password_hash(password: str) -> str:"""假设这是一个加密函数"""return password + 'hash'def save_user_login(user: UserIn) -> UserDb:"""假设这是一个保存用户登录的函数"""password_md5 = get_password_hash(user.password)user_db = UserDb(**user.model_dump(), password_hash=password_md5)print(user_db)return user_db@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):"""创建用户"""user_saved = save_user_login(user)return user_savedif '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

在这里插入图片描述
在这里插入图片描述

4,简化数据模型

在上面的代码中,我们定义了三个数据模型,它们有许多相似的字段,我们可以使用继承来简化这些数据模型。

import uvicornfrom datetime import datetime
from typing import Optionalfrom fastapi import FastAPI
from pydantic import BaseModel, Fieldapp = FastAPI()class UserBase(BaseModel):username: stremail: Optional[str] = Nonephone: Optional[str] = Noneaddress: Optional[str] = Noneclass UserIn(UserBase):password: strclass UserDb(UserBase):password_hash: strupdate_time: datetime = Field(default_factory=datetime.now)class UserOut(UserBase):update_time: datetime# 其余的函数和路由保持不变
def get_password_hash(password: str) -> str:return password + 'hash'def save_user_login(user: UserIn) -> UserDb:password_hash = get_password_hash(user.password)return UserDb(**user.model_dump(exclude={'password'}), password_hash=password_hash)@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):return save_user_login(user)if '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

5,使用多个响应模型

在以下情况下,我们可能需要在响应中返回多个数据模型:

  • 错误处理:当API可能返回成功响应或错误响应时。
  • 条件响应:基于某些条件(如查询参数)返回不同的响应模型。
  • 版本控制:API的不同版本可能返回不同的响应结构。
  • 多态响应:根据请求的资源类型返回不同的响应模型。

这时可以使用 Union 类型将不同的响应模型组合在一起。

from typing import Unionimport uvicorn
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class UserProfile(BaseModel):username: stremail: stris_active: boolclass ErrorMessage(BaseModel):error_code: intmessage: str@app.get("/user/{user_id}", response_model=Union[UserProfile, ErrorMessage])
async def get_user(user_id: int):if user_id == 1:return UserProfile(username="john_doe", email="john@example.com", is_active=True)else:return ErrorMessage(error_code=404, message="User not found")# 另一个例子:根据查询参数返回不同的响应
class BasicUserInfo(BaseModel):username: stremail: strclass DetailedUserInfo(BaseModel):username: stremail: strphone: straddress: str@app.get("/user_info", response_model=Union[BasicUserInfo, DetailedUserInfo])
async def get_user_info(user_id: int, detailed: bool = False):if detailed:return DetailedUserInfo(username="jane_doe", email="jane@example.com",phone="123-456-7890", address="123 Main St")else:return BasicUserInfo(username="jane_doe", email="jane@example.com")if '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

在上面的示例中,我展示了两种使用 Union 类型的情况:

  1. 错误处理:get_user 函数可能返回 UserProfile 或 ErrorMessage。
  2. 条件响应:get_user_info 函数根据 detailed 参数返回 BasicUserInfo 或 DetailedUserInfo。

使用 Union 类型的好处包括:

  • 类型安全:它明确定义了可能的响应类型,有助于静态类型检查。
  • 文档清晰:FastAPI 会自动生成包含所有可能响应的 API 文档。
  • 灵活性:允许在不同情况下返回不同的响应结构,而不需要创建一个包含所有可能字段的大型模型。

需要注意的是,当使用 Union 类型时,客户端需要能够处理不同的响应结构。

查看 API:
在这里插入图片描述
在这里插入图片描述

(三)内置的响应类

在路由处理函数中,我们可以直接返回基础数据类型、用 Union 构造的泛型,或者使用 Pydantic 定义的响应数据模型。
fastapi 会将这些数据转换为 JSON 格式的响应数据,然后通过响应类返回给客户端。

除此之外,FastAPI还提供了一些内置的响应类,用来返回更多类型的响应。

1,纯文本响应——Plain Text Response

fastapi 使用 PlainTextResponse 类来返回纯文本响应,不会对纯文本的内容进行校验与转换。

import uvicornfrom fastapi import FastAPI
from fastapi.responses import PlainTextResponseapp = FastAPI()@app.get("/", response_class=PlainTextResponse)
async def read_root():return "Hello, world!"if __name__ == '__main__':uvicorn.run(app)

2,HTML 响应——HTML Response

fastapi 使用 HTMLResponse 类来返回 HTML 响应,不会对 HTML 的内容进行校验与转换。

import uvicornfrom fastapi import FastAPI
from fastapi.responses import HTMLResponseapp = FastAPI()html_content = """
<html><head><title>FastAPI</title></head><body><h1>Hello, FastAPI!</h1></body>
</html>
"""@app.get("/type1", response_class=HTMLResponse)
async def read_root():"""响应数据的 media type 为 text/html"""return html_content@app.get("/type2")
async def read_root():"""响应数据的 media type 为 application/json"""return HTMLResponse(content=html_content)if __name__ == '__main__':uvicorn.run(app)

3,重定向响应——Redirect Response

fastapi 使用 RedirectResponse 类来返回重定向响应,跳转到指定的 URL。

import uvicornfrom fastapi import FastAPI
from fastapi.responses import RedirectResponseapp = FastAPI()@app.get("/")
def read_root():return RedirectResponse(url='/docs')@app.get("/github")
def read_github():return RedirectResponse(url='https://github.com/')if __name__ == '__main__':uvicorn.run(app)

4,JSON响应——JSON Response

默认情况下,fastapi 使用 json_encoder() 将模型数据转为 JSON 格式,然后使用 JSONResponse 类返回这些数据。

import uvicornfrom typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponseapp = FastAPI()class UserModel(BaseModel):name: strage: intaddress: strdesignation: Optional[str] = None@app.post("/user")
async def update_user(user: UserModel):json_data = jsonable_encoder(user)return JSONResponse(content=json_data)if __name__ == '__main__':uvicorn.run(app)

5,通用响应——Response

当返回的数据不是 JSON 格式时,可以使用 Response 类来返回通用响应。

import uvicornfrom fastapi import FastAPI, Responseapp = FastAPI()@app.get("/item/{item_id}")
async def get_xml_data(item_id: int):data = """<Document><Name>John Doe</Name><Age>25</Age><City>San Francisco</City></Document>"""return Response(content=data, media_type="application/xml")if __name__ == '__main__':uvicorn.run(app)

对于通用响应,我们在实例化 Response 类时需要指定一些额外的参数:

  • content:响应数据的内容;
  • status_code:响应状态码,默认为 200;
  • media_type:响应数据的 media type;自动生成一个 Content-Type 头信息;
  • headers:响应头信息;自动包含一个 Content-Length 头信息,用于指定响应数据的长度。

5,流式响应——StreamingResponse

字节流响应的数据是二进制格式的,用于传输音频、视频、图像等二进制数据。
fastapi 使用 StreamingResponse 类来返回流式响应。

import uvicornfrom fastapi import FastAPI
from fastapi.responses import StreamingResponseapp = FastAPI()@app.get("/get_video")
async def get_file():file_path = "1-1 本周整体内容介绍和学习方法.mp4"file_like = open(file_path, mode="rb")return StreamingResponse(file_like, media_type="video/mp4")if __name__ == '__main__':uvicorn.run(app)

在浏览器访问 http://127.0.0.1:8000/get_video 就可以浏览视频内容了。

6,文件响应——FileResponse

fastapi 使用 FileResponse 类来处理异步文件响应,用于传输文件。
相比于 StreamingResponseFileResponse 更适合传输文件,因为它可以接受更多的参数:

  • path:文件路径;
  • filename:文件名;将自动添加 Content-Disposition 头信息;
  • media_type:文件的 media type;将自动添加 Content-Type 头信息;
  • headers:响应头信息;

两者最大的区别是,FileResponse 是异步读取文件的,不会因为一次性读出文件太大而导致内存溢出。

import uvicornfrom fastapi import FastAPI
from fastapi.responses import FileResponseapp = FastAPI()@app.get("/get_file")
async def get_file():file_path = "教程.md"return FileResponse(file_path, media_type="application/octet-stream", filename="教程.md")if __name__ == '__main__':uvicorn.run(app)

三,响应状态码

(一)HTTP 状态码

1xx: 信息响应

状态码含义描述
100Continue(继续)服务器已收到请求的初始部分,客户端应继续请求。
101Switching Protocols(切换协议)服务器理解并同意客户端的协议切换请求。
102Processing(处理中)服务器已收到并正在处理请求,但无响应可用。
103Early Hints(预先提示)用于与Link头一起返回一些响应头,主要用于预加载。

2xx: 成功响应

状态码含义描述
200OK(成功)请求成功。GET: 资源已被提取并在消息正文中传输。
201Created(已创建)请求成功且服务器创建了新的资源。
202Accepted(已接受)服务器已接受请求,但尚未处理。
203Non-Authoritative Information(非授权信息)服务器已成功处理请求,但返回的信息可能来自另一来源。
204No Content(无内容)服务器成功处理请求,但不需要返回任何实体内容。
205Reset Content(重置内容)服务器成功处理请求,但需要重置文档视图。
206Partial Content(部分内容)服务器成功处理了部分GET请求。

3xx: 重定向消息

状态码含义描述
300Multiple Choices(多种选择)针对请求,服务器可执行多种操作。
301Moved Permanently(永久移动)请求的资源已永久移动到新位置。
302Found(临时移动)请求的资源临时从不同的URI响应请求。
303See Other(查看其他位置)对应当前请求的响应可以在另一个URI上被找到。
304Not Modified(未修改)资源未被修改,可使用缓存版本。
307Temporary Redirect(临时重定向)请求应该被重定向到另一个URI,但将来的请求仍应使用原始URI。
308Permanent Redirect(永久重定向)请求和所有将来的请求应该被重定向到给定的URI。

4xx: 客户端错误响应

状态码含义描述
400Bad Request(错误请求)服务器无法理解请求的语法。
401Unauthorized(未授权)请求要求身份验证。
402Payment Required(需要付款)保留以供将来使用。
403Forbidden(禁止)服务器理解请求但拒绝执行。
404Not Found(未找到)服务器找不到请求的资源。
405Method Not Allowed(方法不允许)请求方法不被允许。
406Not Acceptable(不可接受)服务器无法根据客户端请求的内容特性完成请求。
407Proxy Authentication Required(需要代理授权)请求要求代理的身份认证。
408Request Timeout(请求超时)服务器等候请求时发生超时。
409Conflict(冲突)由于和被请求的资源的当前状态之间存在冲突,请求无法完成。
410Gone(已删除)被请求的资源在服务器上已经不再可用。
411Length Required(需要有效长度)服务器拒绝在没有定义Content-Length头的情况下接受请求。
412Precondition Failed(前提条件失败)服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。
413Payload Too Large(负载过大)请求实体过大,超出服务器的处理能力。
414URI Too Long(URI过长)请求的URI过长,服务器无法处理。
415Unsupported Media Type(不支持的媒体类型)请求的格式不受请求页面的支持。
416Range Not Satisfiable(范围不符合要求)页面无法提供请求的范围。
417Expectation Failed(未满足期望)服务器无法满足Expect的请求头信息。
429Too Many Requests(请求过多)用户在给定的时间内发送了太多的请求。

5xx: 服务器错误响应

状态码含义描述
500Internal Server Error(服务器内部错误)服务器遇到了不知道如何处理的情况。
501Not Implemented(尚未实施)服务器不具备完成请求的功能。
502Bad Gateway(错误网关)作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
503Service Unavailable(服务不可用)由于超载或系统维护,服务器暂时的无法处理客户端的请求。
504Gateway Timeout(网关超时)充当网关或代理的服务器,未及时从上游服务器收到请求。
505HTTP Version Not Supported(HTTP版本不受支持)服务器不支持请求中所用的HTTP协议版本。
506Variant Also Negotiates(变元协商)服务器存在内部配置错误。
507Insufficient Storage(存储空间不足)服务器无法存储完成请求所必须的内容。
508Loop Detected(检测到循环)服务器在处理请求时检测到无限循环。
510Not Extended(未扩展)获取资源所需要的策略并没有被满足。
511Network Authentication Required(要求网络认证)客户端需要进行身份验证才能获得网络访问权限。

HTTP response status codes

(二)fastapi 中的状态码

在fastapi中,有多种方式指定返回不同的响应码。

1,使用response参数

在create_item 函数中,我们接受一个response: Response参数,并直接设 response.status_code。

class Item(BaseModel):name: strprice: float@app.post("/items/")
async def create_item(item: Item, response: Response):if item.price < 0:response.status_code = status.HTTP_400_BAD_REQUESTreturn {"error": "Price cannot be negative"}response.status_code = status.HTTP_201_CREATEDreturn {"message": "Item created successfully", "item": item}

这种方法允许在函数的任何位置设置状态码,同时仍然返回您想要的内容。

2,抛出HTTPException

在read_item函数中,我们使用raise HTTPException来设置状态码和错误详情。

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

这种方法特别适合处理错误情况,因为它会自动停止函数的执行并返回错误响应。

3,返回Response对象

在update_item函数中,我们直接返回一个Response对象,其中包含内容和状态码。

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):if item_id == 0:return Response(content="Item not found", status_code=status.HTTP_404_NOT_FOUND)if item.price > 1000:return Response(content="Price too high", status_code=status.HTTP_400_BAD_REQUEST)return Response(content=f"Item {item_id} updated", status_code=status.HTTP_200_OK)

这种方法给了开发者对响应的完全控制,包括内容和状态码。

4,使用装饰器设置默认状态码

在delete_item函数中,我们使用@app.delete(…, status_code=status.HTTP_204_NO_CONTENT)来设置默认状态码。

@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int, response: Response):if item_id == 0:response.status_code = status.HTTP_404_NOT_FOUNDreturn {"error": "Item not found"}# 如果成功删除,不需要返回内容,状态码会是204return None

如果需要更改状态码,我们仍然可以使用response参数来实现。

5,一些注意事项

  • 使用status模块(如status.HTTP_400_BAD_REQUEST)可以提高代码的可读性。
  • 对于错误处理,HTTPException通常是最清晰和最符合FastAPI风格的选择。
  • 当您需要完全控制响应时,返回Response对象是一个好方法。
  • 当设置了一个非200的状态码时,通常应该返回一些解释性的内容。

四,处理异常

在处理请求出错的情况下,需要通过响应来向客户端返回错误提示。

(一)使用 HTTPException

FastAPI的HTTPException是一个用于在API中引发HTTP错误的异常类。它允许你在代码中明确地抛出HTTP错误,以便向客户端返回适当的错误响应。

from fastapi import FastAPI, HTTPExceptionapp = FastAPI()@app.get("/items/{item_id}")
async def read_item(item_id: int):if item_id not in some_items_db:raise HTTPException(status_code=404, detail="Item not found")return {"item": some_items_db[item_id]}

HTTPException 是额外包含了和 API 有关数据的常规 Python 异常。因为是 Python 异常,所以不能 return,只能 raise。

如在调用路径操作函数里的工具函数时,触发了 HTTPException,FastAPI
就不再继续执行路径操作函数中的后续代码,而是立即终止请求,并把HTTPException 的 HTTP 错误发送至客户端。

主要参数:

  • status_code: HTTP状态码(如404, 400, 500等)
  • detail: 错误的详细描述
  • headers: 可选的额外响应头

(二)全局异常

代码中通常要处理大量异常,如果所有异常都 raise HTTPException,那么代码将更加复杂,因此 fastapi 提供了全局异常处理器来自定义不同类型的异常,实现逻辑代码与异常处理代码的分离。

import uvicorn
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponseapp = FastAPI()# 自定义异常类
class CustomException(Exception):def __init__(self, name: str):self.name = name# 为HTTPException定义异常处理器
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):return JSONResponse(status_code=exc.status_code,content={"message": f"HTTP error occurred: {exc.detail}"})# 为CustomException定义异常处理器
@app.exception_handler(CustomException)
async def custom_exception_handler(request: Request, exc: CustomException):return JSONResponse(status_code=418,content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."})# 路由处理函数,可能会抛出HTTPException
@app.get("/items/{item_id}")
async def read_item(item_id: int):if item_id == 3:raise HTTPException(status_code=404, detail="Item not found")return {"item_id": item_id}# 路由处理函数,可能会抛出CustomException
@app.get("/unicorns/{name}")
async def read_unicorn(name: str):if name == "yolo":raise CustomException(name=name)return {"unicorn_name": name}# 全局异常处理器
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):return JSONResponse(status_code=500,content={"message": f"An unexpected error occurred: {str(exc)}"})if '__main__' == __name__:uvicorn.run(app, host='127.0.0.1', port=8088)

解释一下这个代码示例的主要部分:

  1. 异常处理器装饰器:
    @app.exception_handler(ExceptionType) 用来定义一个异常处理器。ExceptionType 可以是任何异常类,如 HTTPException, CustomException, 或者甚至是基础的 Exception 类。

  2. 异常处理器函数:
    这些函数接收两个参数: request (当前的请求对象) 和 exc (捕获到的异常实例)。它们返回一个响应对象,通常是 JSONResponse。

  3. HTTPException 处理器:
    这个处理器捕获所有的 HTTPException,并返回一个包含状态码和错误消息的 JSON 响应。

  4. 自定义异常处理器:
    我们定义了一个 CustomException 类和相应的处理器。这展示了如何处理应用特定的异常。

  5. 路由处理函数:
    示例中包含了两个路由处理函数,分别可能抛出 HTTPException 和 CustomException。

  6. 全局异常处理器:
    最后,我们定义了一个处理所有未被其他处理器捕获的异常的全局处理器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/51032.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全开源收银系统源码-支付通道

1.收银系统开发语言 核心开发语言: PHP、HTML5、Dart后台接口: PHP7.3后合管理网站: HTML5vue2.0element-uicssjs线下收银台&#xff08;安卓/PC收银、安卓自助收银&#xff09;: Dart3框架&#xff1a;Flutter 3.19.6助手: uniapp商城: uniapp 2.支付通道 智慧新零售收银系统…

一下午连续故障两次,谁把我们接口堵死了?!

唉。。。 大家好&#xff0c;我是程序员鱼皮。又来跟着鱼皮学习线上事故的处理经验了喔&#xff01; 事故现场 周一下午&#xff0c;我们的 编程导航网站 连续出现了两次故障&#xff0c;每次持续半小时左右&#xff0c;现象是用户无法正常加载网站&#xff0c;一直转圈圈。 …

小白学习webgis的详细路线

推荐打开boss直聘搜索相关岗位&#xff0c;查看岗位要求&#xff0c;对症下药是最快的。 第一阶段&#xff1a;基础知识准备 计算机基础 操作系统&#xff1a;理解Windows、Linux或macOS等操作系统的基本操作&#xff0c;学会使用命令行界面。网络基础&#xff1a;掌握TCP/I…

平价不入耳运动耳机哪款最好?五款回购榜优品种草

许多有健身运动习惯的朋友在选择耳机时会优先考虑不入耳耳机&#xff0c;因为它佩戴舒适&#xff0c;稳固性和安全性更高&#xff0c;不仅在运动时不会轻易掉落&#xff0c;还能够方便我们在进行户外运动时接收外界的声音。那么&#xff0c;平价不入耳运动耳机哪款最好&#xf…

docker-compose 根据yaml拉取镜像出问题

在学习go微服务时&#xff0c;用docker-compose启动nacos以及对应的mysql时出现上面的问题&#xff0c; 使用的yaml如下 version: "3.8" services:nacos:image: nacos/nacos-server:${NACOS_VERSION}container_name: nacos-standalone-mysqlenv_file:- ../env/cust…

javaEE(1)

一. Web开发概述 Web开发:指的是从网页中向后端程序发送请求,与后端程序进行交互 Web服务器:是一种软件,向浏览器等Web客户端提供文档等数据,实现数据共享,它是一个容器,是一个连接用户和程序之间的中间键 二. Web开发环境搭建 我们要实现前后端交互,首先需要中间键Web服务…

重生之“我打数据结构,真的假的?”--6.排序

1.排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使⼀串记录&#xff0c;按照其中的某个或某些关键字的⼤⼩&#xff0c;递增或递减的排列起来的 操作。 1.1排序分类 2.排序算法实现 2.1插入排序 直接插⼊排序是⼀种简单的插⼊排序法&#xff0c;其基本思想是&#…

【Nginx】Windows生成ssl证书,Nginx反向代理HTTPS

下载 OpenSSL 环境 Windows、Linux, 证书我是在Windows上生成的自签证书。 https://slproweb.com/products/Win32OpenSSL.htmlWin64 OpenSSL v3.3.1 EXE&#xff08;这个&#xff09; | MSISelect Additional Tasks页面勾选 The OpenSSL binaries (/bin) directory 然后将Op…

一篇文章学完Python基础

1. 字符串 str1 "Hello" str2 " World" print(str1 str2) # 输出&#xff1a;HelloWorld 1.1 字符替换 text "Hello, World!" new_text text.replace("World", "Python") print(new_text) # 输出&#xff1a;…

2024第八届自然语言处理与信息检索国际会议 (NLPIR 2024)即将召开!

2024第八届自然语言处理与信息检索国际会议 (NLPIR 2024)将于2024年12月13-15日在日本冈山的冈山大学举行。NLPIR 2024将为自然语言处理与信息检索领域的专家学者提供一个交流与合作的平台&#xff0c;推动该领域的学术进步和技术创新。同时&#xff0c;本次会议也将为相关企业…

Golang 高性能 Websocket 库 gws 使用与设计(一)

前言 大家好这里是&#xff0c;白泽&#xff0c;这期分析一下 golang 开源高性能 websocket 库 gws。 视频讲解请关注&#x1f4fa;B站&#xff1a;白泽talk 介绍 gws&#xff1a;https://github.com/lxzan/gws &#xff5c;GitHub &#x1f31f; 1.2k&#xff0c;高性能的 …

0724,select +tcp 聊天室喵

目录 TCP协议喵 723__01&#xff1a;使用select实现一个基于UDP的一对一即时聊天程序。 001: 002: TIMEWAI OR BUG 721作业&#xff1a; 01&#xff1a;在一对一聊天的基础上&#xff0c;使用select实现一对多的回显服务。&#xff08;回显服务即接收到客户端发送的数…

Pyppeteer 的使用

puppeteer 是基于Node.js 开发的一个工具, 有了它&#xff0c;我们可以利用 JavaScript 控制 Chrome 浏览器的一些操作。当然&#xff0c; puppeteer 也可以用于网络爬虫&#xff0c;其 API 及其完善&#xff0c;功能非常强大。 Pyppeteer 其实是 puppeteer 的 python 实现&…

.net 连接达梦数据库开发环境部署

.net 开发环境部署 1. 环境准备 测试工具 Visual Studio2022 数据库版本 dm8 2. 搭建过程 1 &#xff09;创建新项目 2 &#xff09;选择创建空项目 3 &#xff09;配置新项目 4 &#xff09;右键 DM1 新建一个项 5 &#xff09;加 载 驱 动 &#xff0c; 新 建 …

0722_驱动3 地址映射驱动点灯

一、为什么需要地址映射 在芯片手册上查看到的地址属于物理地址&#xff0c;在硬件层 在内核空间地址属于虚拟地址&#xff0c;在内核层 在驱动中&#xff0c;操作的是虚拟地址 需要将物理地址《--mmu内存管理单元--》虚拟地址映射 二、映射API接口 void *ioremap(unsigned lon…

backtrace

介绍 arm平台的调用栈与x86平台的调用栈大致相同&#xff0c;稍微有些区别&#xff0c;主要在于栈帧的压栈内容和传参方式不同。在arm平台的不同程序&#xff0c;采用的编译选项不同&#xff0c;程序运行期间的栈帧也会不同。有些工具在对arm的调用栈回溯时&#xff0c;可能会…

电商项目之如何判断线程池是否执行完所有任务

文章目录 1 问题背景2 前言3 4种常用的方法4 代码4.1 isTerminated()4.2 线程池的任务总数是否等于已执行的任务数4.3 CountDownLatch计数器4.4 CyclicBarrier计数器 1 问题背景 真实生产环境的电商项目&#xff0c;常使用线程池应用于执行大批量操作达到高性能的效果。应用场景…

Dify中语音和文字间转换问题的一种暂时注释方式

本文主要解释了Dify中语音和文字间转换可能会遇到的问题&#xff0c;并给出了一种暂时注释的解决方案。 一.文本转语音可能问题 本地部署文本转语音时&#xff0c;如果遇到如下问题&#xff0c;安装ffmpeg即可。但是如果安装后&#xff0c;重启系统还是遇到这个问题该如何办&…

分库分表——从理论到最佳实践

目录 1、为什么要分库分表&#xff1f;2、切分方案有哪些&#xff1f;2.1 分库2.1.1 垂直分库2.1.2 水平分库 2.2 分表2.2.1 垂直分表2.2.2 水平分表 2.3 分库分表 3、数据水平分片方法3.1 Hash分片3.2 一致性Hash分片3.3 Range分片 4、分库分表的挑战4.1 分布式id4.2 分布式事…

LLM:归一化 总结

一、Batch Normalization 原理 Batch Normalization 是一种用于加速神经网络训练并提高稳定性的技术。它通过在每一层网络的激活值上进行归一化处理&#xff0c;使得每一层的输入分布更加稳定&#xff0c;从而加速训练过程&#xff0c;并且减轻了对参数初始化的依赖。 公式 …