三周精通FastAPI:16 Handling Errors处理错误

官网文档:https://fastapi.tiangolo.com/zh/tutorial/handling-errors

处理错误¶

某些情况下,需要向客户端返回错误提示。

这里所谓的客户端包括前端浏览器、其他应用程序、物联网设备等。

需要向客户端返回错误提示的场景主要如下:

  • 客户端没有执行操作的权限
  • 客户端没有访问资源的权限
  • 客户端要访问的项目不存在
  • 等等 ...

遇到这些情况时,通常要返回 4XX(400 至 499)HTTP 状态码

4XX 状态码与表示请求成功的 2XX(200 至 299) HTTP 状态码类似。

只不过,4XX 状态码表示客户端发生的错误。

大家都知道「404 Not Found」错误,还有调侃这个错误的笑话吧?

使用 HTTPException

向客户端返回 HTTP 错误响应,可以使用 HTTPException

from fastapi import FastAPI, HTTPExceptionapp = FastAPI()items = {"foo": "The Foo Wrestlers"}@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]}

导入 HTTPException

from fastapi import FastAPI, HTTPException

触发 HTTPException

HTTPException 是额外包含了和 API 有关数据的常规 Python 异常。

因为是 Python 异常,所以不能 return,只能 raise

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

在介绍依赖项与安全的章节中,您可以了解更多用 raise 异常代替 return 值的优势。本例中,客户端用 ID 请求的 item 不存在时,触发状态码为 404 的异常:

@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]}

响应结果¶

请求为 http://example.com/items/fooitem_id 为 「foo」)时,客户端会接收到 HTTP 状态码 - 200 及如下 JSON 响应结果:

{ "item": "The Foo Wrestlers" }

但如果客户端请求 http://example.com/items/baritem_id 「bar」 不存在时),则会接收到 HTTP 状态码 - 404(「未找到」错误)及如下 JSON 响应结果:

{ "detail": "Item not found" }

"提示"

触发 HTTPException 时,可以用参数 detail 传递任何能转换为 JSON 的值,不仅限于 str

还支持传递 dictlist 等数据结构。

FastAPI 能自动处理这些数据,并将之转换为 JSON。

添加自定义响应头¶

有些场景下要为 HTTP 错误添加自定义响应头。例如,出于某些方面的安全需要。

一般情况下可能不会需要在代码中直接使用响应头。但对于某些高级应用场景,还是需要添加自定义响应头:

from fastapi import FastAPI, HTTPExceptionapp = FastAPI()items = {"foo": "The Foo Wrestlers"}@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]}

安装自定义异常处理器¶

添加自定义处理器,要使用 Starlette 的异常工具。

假设要触发的自定义异常叫作 UnicornException

且需要 FastAPI 实现全局处理该异常。此时,可以用 @app.exception_handler() 添加自定义异常控制器:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponseclass UnicornException(Exception):def __init__(self, name: str):self.name = nameapp = FastAPI()@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}

请求 /unicorns/yolo 时,路径操作会触发 UnicornException

但该异常将会被 unicorn_exception_handler 处理。

接收到的错误信息清晰明了,HTTP 状态码为 418,JSON 内容如下:

{"message": "Oops! yolo did something. There goes a rainbow..."} 

"技术细节"

from starlette.requests import Request 和 from starlette.responses import JSONResponse 也可以用于导入 Request 和 JSONResponse

FastAPI 提供了与 starlette.responses 相同的 fastapi.responses 作为快捷方式,但大部分响应操作都可以直接从 Starlette 导入。同理,Request 也是如此。

覆盖默认异常处理器¶

FastAPI 自带了一些默认异常处理器。

触发 HTTPException 或请求无效数据时,这些处理器返回默认的 JSON 响应结果。

不过,也可以使用自定义处理器覆盖默认异常处理器。

覆盖请求验证异常¶

请求中包含无效数据时,FastAPI 内部会触发 RequestValidationError

该异常也内置了默认异常处理器。

覆盖默认异常处理器时需要导入 RequestValidationError,并用 @app.excption_handler(RequestValidationError) 装饰异常处理器。这样,异常处理器就可以接收 Request 与异常。

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPExceptionapp = FastAPI()@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):return PlainTextResponse(str(exc.detail), status_code=exc.status_code)@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):return PlainTextResponse(str(exc), status_code=400)@app.get("/items/{item_id}")
async def read_item(item_id: int):if item_id == 3:raise HTTPException(status_code=418, detail="Nope! I don't like 3.")return {"item_id": item_id}

访问 /items/foo,可以看到默认的 JSON 错误信息:

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

被替换为了以下文本格式的错误信息:

1 validation error path -> item_id value is not a valid integer (type=type_error.integer) 

RequestValidationError vs ValidationError

"警告"

如果您觉得现在还用不到以下技术细节,可以先跳过下面的内容。

RequestValidationError 是 Pydantic 的 ValidationError 的子类。

FastAPI 调用的就是 RequestValidationError 类,因此,如果在 response_model 中使用 Pydantic 模型,且数据有错误时,在日志中就会看到这个错误。

但客户端或用户看不到这个错误。反之,客户端接收到的是 HTTP 状态码为 500 的「内部服务器错误」。

这是因为在响应或代码(不是在客户端的请求里)中出现的 Pydantic ValidationError 是代码的 bug。

修复错误时,客户端或用户不能访问错误的内部信息,否则会造成安全隐患。

覆盖 HTTPException 错误处理器¶

同理,也可以覆盖 HTTPException 处理器。例如,只为错误返回纯文本响应,而不是返回 JSON 格式的内容:

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPExceptionapp = FastAPI()@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):return PlainTextResponse(str(exc.detail), status_code=exc.status_code)@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):return PlainTextResponse(str(exc), status_code=400)@app.get("/items/{item_id}")
async def read_item(item_id: int):if item_id == 3:raise HTTPException(status_code=418, detail="Nope! I don't like 3.")return {"item_id": item_id}

"技术细节"

还可以使用 from starlette.responses import PlainTextResponse

FastAPI 提供了与 starlette.responses 相同的 fastapi.responses 作为快捷方式,但大部分响应都可以直接从 Starlette 导入。

使用 RequestValidationError 的请求体¶

RequestValidationError 包含其接收到的无效数据请求的 body 。开发时,可以用这个请求体生成日志、调试错误,并返回给用户。

from fastapi import FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModelapp = FastAPI()@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):return JSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),)class Item(BaseModel):title: strsize: int@app.post("/items/")
async def create_item(item: Item):return item

现在试着发送一个无效的 item,例如:

{ "title": "towel", "size": "XL" } 

收到的响应包含 body 信息,并说明数据是无效的:

{"detail": [{"loc": ["body","size"],"msg": "value is not a valid integer","type": "type_error.integer"}],"body": {"title": "towel","size": "XL"}
}

FastAPI HTTPException vs Starlette HTTPException

FastAPI 也提供了自有的 HTTPException

FastAPI 的 HTTPException 继承自 Starlette 的 HTTPException 错误类。

它们之间的唯一区别是,FastAPI 的 HTTPException 可以在响应中添加响应头。

OAuth 2.0 等安全工具需要在内部调用这些响应头。

因此你可以继续像平常一样在代码中触发 FastAPI 的 HTTPException 。

但注册异常处理器时,应该注册到来自 Starlette 的 HTTPException

这样做是为了,当 Starlette 的内部代码、扩展或插件触发 Starlette HTTPException 时,处理程序能够捕获、并处理此异常。

注意,本例代码中同时使用了这两个 HTTPException,此时,要把 Starlette 的 HTTPException命名为 StarletteHTTPException

from starlette.exceptions import HTTPException as StarletteHTTPException 

复用 FastAPI 异常处理器¶

FastAPI 支持先对异常进行某些处理,然后再使用 FastAPI 中处理该异常的默认异常处理器。从 fastapi.exception_handlers 中导入要复用的默认异常处理器:

from fastapi import FastAPI, HTTPException
from fastapi.exception_handlers import (http_exception_handler,request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPExceptionapp = FastAPI()@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):print(f"OMG! An HTTP error!: {repr(exc)}")return await http_exception_handler(request, exc)@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):print(f"OMG! The client sent invalid data!: {exc}")return await request_validation_exception_handler(request, exc)@app.get("/items/{item_id}")
async def read_item(item_id: int):if item_id == 3:raise HTTPException(status_code=418, detail="Nope! I don't like 3.")return {"item_id": item_id}

虽然,本例只是输出了夸大其词的错误信息。

但也足以说明,可以在处理异常之后再复用默认的异常处理器。

实践

自定义异常处理

源代码

将代码写入errors.py

​
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponseclass UnicornException(Exception):def __init__(self, name: str):self.name = nameapp = FastAPI()@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}​

启动服务

uvicorn errors:app --reload

浏览测试

浏览:http://127.0.0.1:8000/unicorns/Bruce_Lee

显示:

{"unicorn_name":"Bruce_Lee"}

浏览:http://127.0.0.1:8000/unicorns/yolo

显示:

{"message":"Oops! yolo did something. There goes a rainbow..."}

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

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

相关文章

FastAPI、langchain搭建chatbot,langgraph实现历史记录

环境:openEuler、python 3.11.6、Azure openAi、langchain 0.3.3、langgraph 0.2.38 背景:基于FastAPI、langchain实现一个QA系统,要求实现历史记录以及存储特征信息 时间:20241022 说明:在历史记录的存储中&…

R语言机器学习算法实战系列(十四): CatBoost分类算法+SHAP值 (categorical data gradient boosting)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍CatBoost的原理CatBoost的步骤教程下载数据加载R包导入数据数据预处理数据描述数据切割设置数据对象调节参数训练模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC Curv…

mysql 通过GROUP BY 聚合并且拼接去重另个字段

我的需求: 我想知道同一个手机号出现几次,并且手机号出现在哪些地方。下面是要的效果。 源数据: CREATE TABLE bank (id bigint(20) unsigned NOT NULL AUTO_INCREMENT,user_id int(11) NOT NULL DEFAULT 0,tel varchar(255) COLLATE utf8mb4_unicode_…

【自然语言处理】BERT模型

BERT:Bidirectional Encoder Representations from Transformers BERT 是 Google 于 2018 年提出的 自然语言处理(NLP)模型,它基于 Transformer 架构的 Encoder 部分。BERT 的出现极大提升了 NLP 任务的性能,如问答系…

Python | Leetcode Python题解之第509题斐波那契数

题目&#xff1a; 题解&#xff1a; class Solution:def fib(self, n: int) -> int:if n < 2:return nq [[1, 1], [1, 0]]res self.matrix_pow(q, n - 1)return res[0][0]def matrix_pow(self, a: List[List[int]], n: int) -> List[List[int]]:ret [[1, 0], [0, …

自动化部署-02-jenkins部署微服务

文章目录 前言一、配置SSH-KEY1.1 操作jenkins所在服务器1.2 操作github1.3 验证 二、服务器安装git三、jenkins页面安装maven四、页面配置自动化任务4.1 新建任务4.2 选择4.3 配置参数4.4 配置脚本 五、执行任务5.1 点击执行按钮5.2 填写参数5.3 查看日志 六、查看服务器文件七…

51单片机STC8G串口Uart配置

测试环境 单片机型号&#xff1a;STC8G1K08-38I-TSSOP20&#xff0c;其他型号请自行测试&#xff1b; IDE&#xff1a;KEIL C51&#xff1b; 寄存器配置及主要代码 STC8G系列单片机具有4个全双工异步串行通信接口&#xff1b;本文以串口1为例&#xff0c;串口1有4种工作方式…

java疫苗发布和接种预约系统源码(springboot)

项目简介 疫苗发布和接种预约系统实现了以下功能&#xff1a; 疫苗发布和接种预约系统的主要使用者分为&#xff1a; 管理员对公告信息&#xff0c;医院信息&#xff0c;疫苗信息&#xff0c;医生信息&#xff0c;用户信息&#xff0c;论坛帖子信息以及预约接种信息等信息进行…

C语言程序设计:现代设计方法习题笔记《chapter5》下篇

第七题 题目分析&#xff1a;求最大最小值转换为条件判断问题&#xff0c;最大值有四种可能&#xff0c;最小值相应有三种情况&#xff0c;给出下列代码。 示例代码&#xff1a; #include <stdio.h>int main() {int num1, num2, num3, num4; // 定义四个变量来存储输入…

nfs服务部署案例

目录 nfs服务介绍 案例信息 服务端部署 安装服务 启动服务 修改配置文件 重新加载配置文件 创建存储目录 客户端部署 安装服务 挂载nfs目录 测试 nfs服务介绍 nfs是网络文件系统&#xff0c;类似与windows的共享文件&#xff0c;用于存储文件。 nfs依赖于rpc服务才…

【C++】抱C++中的函数式编程:使用`std::function`和Lambda表达式简化代码

C自C11标准引入了lambda表达式、std::function和std::bind&#xff0c;为开发者带来了强大的函数式编程特性。函数式编程让代码更加灵活、简洁、可重用&#xff0c;并使得开发者可以轻松处理回调、事件驱动编程和更复杂的函数组合。本文将详细介绍C中函数式编程的关键工具&…

解码专业术语——应用系统开发项目中的专业词汇解读

文章目录 引言站点设置管理具体要求包括&#xff1a; Footer管理基于URL的权限控制利用数据连接池优化数据库操作什么是数据连接池&#xff1f;优化的优势 利用反射改造后端代码&#xff0c;AJAX反射的作用及其在后端代码中的应用AJAX 实现前后端无刷新交互 引言 创新实践项目二…

Linux常用命令1

切换目录 cd [rootlocalhost menge]# cd /[rootlocalhost /]# cd: cd [-L|[-P [-e]] [-]] [目录] 查看当前的目录 pwd 浏览目录内容 ls ls浏览后颜色表示 白色&#xff1a;普通文件 蓝色&#xff1a;目录 红色&#xff1a;压缩包文件 黄色&#xff1a;设备文件 绿…

Python浪漫之画一个圆月亮

效果图&#xff1a; 完整代码&#xff1a; import turtle import time# 创建一个画布 screen turtle.Screen() screen.bgcolor("darkblue") # 设置背景为深蓝色# 创建一个海龟&#xff08;turtle&#xff09;用于绘制月亮 moon turtle.Turtle() moon.color("…

Axure设置面板状态——元件动作二

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01;因为有事断更了三天&#xff0c;从今天开始又回来了&#xff0c;继续为大家分享Axure相关知识点。 课程主题&#xff1a;设置面板状态 主要内容&#xff1a;State状态、推…

UML 总结(基于《标准建模语言UML教程》)

定义 UML 又称为统一建模语言或标准建模语言&#xff0c;是一种标准的图形化建模语言&#xff0c;它是面向对象分析与设计的一种标准表示。尽管UML 本身没有对过程有任何定义&#xff0c;但UML 对任何使用它的方法&#xff08;或过程&#xff09;提出的要求是&#xff1a;支持用…

springboot入门学习笔记

在我们创建了一个Springboot项目之后&#xff0c;我们会看到有很多文件夹和文件 Springboot程序中各类文件的意义 一.pom.xml 在 Spring Boot 项目中&#xff0c;pom.xml&#xff08;Project Object Model&#xff09;文件是 Maven 构建工具的核心配置文件。起到项目信息定义…

S-Function

目录 S-Function介绍 生成S-Function的三种常用手段 使用手写S-函数合并定制代码 使用S-Function Builder块合并定制代码 使用代码继承工具合并定制代码 S-Function介绍 我们可以使用S-Function扩展Simulink对仿真和代码生成的支持。例如&#xff0c;可以使用它们&#xf…

ELK之路第一步——Elasticsearch集群的搭建以及踩坑记录

elasticSearch集群 前言一、架构二、下载三、虚拟机相关设置3.1 创建es用户3.2 为建es用户赋权sudo3.3 更换es目录所属用户 四、Elasticsearch配置文件修改4.1 修改elasticsearch.yml4.2 修改jvm.options4.3 修改jdk路径 五、启动六、启动报错七、可视化界面cerebro 前言 Elk&…

二进制方式部署k8s集群

目标任务: 1、Kubernetes集群部署架构规划 2、部署Etcd数据库集群 3、在Node节点安装Docker 4、部署Flannel网络插件 5、在Master节点部署组件(api-server,schduler,controller-manager) 6、在Node节点部署组件(kubelet,kube-proxy) 7、查看集群状态 8、运行⼀个测…