如何在 FastAPI 中自定义特定路由的错误响应?

2024-01-15

我想做一个HTTPFastAPI 中需要特定的端点Header,产生一个定制的response代码时Header不存在,并且显示Header as required在 FastAPI 生成的 OpenAPI 文档中。

例如,如果我将此端点设置为需要some-custom-header:

@app.post("/")
async def fn(some_custom_header: str = Header(...)):
    pass

当客户请求缺少时some-custom-header,服务器会产生一个response有错误代码422 Unprocessable entity https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422。不过我希望能够将其更改为401 Unauthorized https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401。换句话说,我想定制RequestValidationError对于该特定路线在我的 API 中。

我认为一个可能的解决方案是使用Header(None),并进行测试None在函数体中,但不幸的是,这导致 OpenAPI 文档表明标头是optional.


Option 1

如果你不介意拥有Header显示为Optional in OpenAPI/Swagger UI 自动文档 https://fastapi.tiangolo.com/tutorial/first-steps/#interactive-api-docs,这将很简单,如下所示:

from fastapi import Header, HTTPException
@app.post("/")
def some_route(some_custom_header: Optional[str] = Header(None)):
    if not some_custom_header:
        raise HTTPException(status_code=401, detail="Unauthorized")
    return {"some-custom-header": some_custom_header}

Option 2

但是,既然您想要Header出现为required在 OpenAPI 中,您应该覆盖默认的异常处理程序。当请求包含无效数据时,FastAPI 会在内部引发RequestValidationError https://fastapi.tiangolo.com/tutorial/handling-errors/#override-the-default-exception-handlers。因此,您需要重写RequestValidationError, 哪个包含收到的带有无效数据的正文 https://fastapi.tiangolo.com/tutorial/handling-errors/#use-the-requestvalidationerror-body.

Since RequestValidationError是 Pydantic 的子类ValidationError https://pydantic-docs.helpmanual.io/usage/models/#error-handling,您可以访问上面链接中显示的错误,以便检查您的自定义是否正确Header包含在错误中(如果是这样,则意味着请求中缺少该内容,或者不属于该错误)str类型),因此返回您的自定义错误响应。如果您的定制Header (i.e., some_custom_header在下面的示例中)是该特定端点中的唯一参数,那么它是not执行上述检查(并在下面演示)所必需的,就好像RequestValidationError被提出,它只会针对该参数。

Example

from fastapi import FastAPI, Request, Header, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder

app = FastAPI()
routes_with_custom_exception = ['/']

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    if request.url.path in routes_with_custom_exception:
        # check whether the error relates to the `some_custom_header` parameter
        for err in exc.errors():
            if err['loc'][0] == 'header' and err['loc'][1] == 'some-custom-header':
                return JSONResponse(content={'401': 'Unauthorized'}, status_code=401)
            
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=jsonable_encoder({'detail': exc.errors(), 'body': exc.body}),
    )

@app.get('/')
def some_route(some_custom_header: str = Header(...)):
    return {'some-custom-header': some_custom_header}

Option 3

另一种解决方案是使用子应用程序 https://fastapi.tiangolo.com/advanced/sub-applications/(受到讨论的启发here https://github.com/tiangolo/fastapi/issues/1174)。您可以创建一个子应用程序(或更多,如果需要)并将其安装到主应用程序 - 其中将包括需要自定义的路由Header;因此,重写exception_handler for RequestValidationError在该子应用程序中仅适用于这些路线,而无需检查request.url.path,如之前的解决方案所示,并像往常一样让主应用程序具有剩余的路线。根据文档 https://fastapi.tiangolo.com/advanced/sub-applications/#mounting-a-fastapi-application:

安装 FastAPI 应用程序

“挂载”是指在一个应用程序中添加一个完全“独立”的应用程序。 特定路径,然后负责处理该路径下的所有内容 路径,与路径操作在该子应用程序中声明。

Example

Note:如果您安装了子应用程序(即subapi在下面的示例中)使用'/'路径,您将无法访问subapi at http://127.0.0.1:8000/docs http://127.0.0.1:8000/docs,因为该页面上的 API 文档仅包含主应用程序的路由。此外,它还会干扰'/'主 API 的路由(如果主 API 中存在这样的路由),并且因为FastAPI 中端点的顺序很重要 https://stackoverflow.com/a/74498663/17865804,发出请求http://127.0.0.1:8000/实际上会调用主API的相应路由(如下所示)。因此,您宁愿安装subapi使用不同的路径,例如'/sub',如下所示,并访问子 API 文档:http://127.0.0.1:8000/sub/docs http://127.0.0.1:8000/sub/docs。下面还给出了一个 Python 请求示例,演示了如何测试应用程序。

from fastapi import FastAPI, Request, Header
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get('/')
async def main():
    return {'message': 'Hello from main API'}
    

subapi = FastAPI()
   
@subapi.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    # if there are other parameters defined in the endpoint other than
    # `some_custom_header`, then perform a check, as demonstrated in Option 2
    return JSONResponse(content={'401': 'Unauthorized'}, status_code=401)

    
@subapi.get('/')
async def sub_api_route(some_custom_header: str = Header(...)):
    return {'some-custom-header': some_custom_header}    


app.mount('/sub', subapi)

测试上面的例子

import requests

# Test main API
url = 'http://127.0.0.1:8000/'

r = requests.get(url=url)
print(r.status_code, r.json())

# Test sub API
url = 'http://127.0.0.1:8000/sub/'

r = requests.get(url=url)
print(r.status_code, r.json())

headers = {'some-custom-header': 'this is some custom header'}
r = requests.get(url=url, headers=headers)
print(r.status_code, r.json())

Option 4

进一步的解决方案是使用APIRouter https://fastapi.tiangolo.com/tutorial/bigger-applications/#apirouter with a custom APIRoute class https://fastapi.tiangolo.com/advanced/custom-request-and-route/#custom-apiroute-class-in-a-router,如选项 2 所示这个答案 https://stackoverflow.com/a/73464007/17865804,并在 a 内处理请求try-except块(将用于捕获RequestValidationError例外情况),如中所述FastAPI 的文档 https://fastapi.tiangolo.com/advanced/custom-request-and-route/#accessing-the-request-body-in-an-exception-handler。如果出现异常,您可以根据需要处理错误,并返回自定义响应。

Example

from fastapi import FastAPI, APIRouter, Response, Request, Header, HTTPException
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from fastapi.routing import APIRoute
from typing import Callable

class ValidationErrorHandlingRoute(APIRoute):
    def get_route_handler(self) -> Callable:
        original_route_handler = super().get_route_handler()

        async def custom_route_handler(request: Request) -> Response:
            try:
                return await original_route_handler(request)
            except RequestValidationError as e:
                # if there are other parameters defined in the endpoint other than
                # `some_custom_header`, then perform a check, as demonstrated in Option 2
                raise HTTPException(status_code=401, detail='401 Unauthorized')
                            
        return custom_route_handler


app = FastAPI()
router = APIRouter(route_class=ValidationErrorHandlingRoute)


@app.get('/')
async def main():
    return {'message': 'Hello from main API'}
    

@router.get('/custom')
async def custom_route(some_custom_header: str = Header(...)):
    return {'some-custom-header': some_custom_header}


app.include_router(router) 

测试上面的例子

import requests

# Test main API
url = 'http://127.0.0.1:8000/'

r = requests.get(url=url)
print(r.status_code, r.json())

# Test custom route
url = 'http://127.0.0.1:8000/custom'

r = requests.get(url=url)
print(r.status_code, r.json())

headers = {'some-custom-header': 'this is some custom header'}
r = requests.get(url=url, headers=headers)
print(r.status_code, r.json())
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 FastAPI 中自定义特定路由的错误响应? 的相关文章

  • 递归单元测试发现

    我有一个带有目录 tests 的包 我在其中存储单元测试 我的包裹看起来像 LICENSE models init py README md requirements txt tc py tests db test employee py
  • 使用 pip 或 conda 来管理包? [复制]

    这个问题在这里已经有答案了 我已经使用 matlab 进行机器学习很长一段时间了 最 近切换到 python 并使用其包管理器 pip 安装某些包并成功安装了许多包 几天前 我开始使用 conda 我以前安装的所有软件包都被覆盖 我真的很想
  • 如何在基于其他数据帧的数据帧中创建联接?

    我有 2 个数据框 一份包含学生批次详细信息 另一份包含分数 我想加入 2 个数据框 数据框1包含 s1 s2 s3 Stud1 Stud2 Stud3 Stud2 Stud4 Stud1 Stud1 Stud3 Stud4 数据框2包含
  • 使用 Python pdb 检查未处理异常原因的最简单方法是什么?

    我刚刚将所有单元测试数据从 JSON 转换为 YAML 现在代码中的某处引发了异常 更具体地说 这是打印的回溯 Traceback most recent call last File tests test addrtools py lin
  • 有没有纯Python的表类?

    我正在构建一个需要分析表格数据的应用程序 我想执行一些列操作 例如重命名列 删除列以及根据现有列的值计算新列的能力 我的第一选择是 Pandas 之类的东西 但是一个限制是这个项目必须是跨平台的并且非常容易在 virtualenv 中部署
  • 每当我尝试在 VPS 上使用 Discord 机器人登录时,都会收到“SSL:Certificate_verify_failed”

    我正在将我的机器人从旧的 坏掉的笔记本电脑转移到合适的 VPS 我使用的是较旧的异步版本的 Discord py 0 16 0 因为我在重写之前很长时间就开始研究这个东西了 而且我对 Linux 没有太多经验 因此迁移到 Windows S
  • ValueError:无法将 DatetimeIndex 转换为 dtype datetime64[us]

    我正在尝试为 S P 500 ETF 创建一个包含 30 分钟数据的 PostgreSQL 表 spy30new 用于测试新插入的数据 来自具有 15 分钟数据的多个股票的表 全部 15 个 all15 在 dt 时间戳 和 instr 股
  • 使用 python 将 bibtex 文件转换为 html (也许是 pybtex?)

    您好 我想解析 bibtex 出版物文件并对特定字段 例如年份 进行排序并过滤某些内容 然后将其放在网站上 我遇到了 pybtex 它可以读取和解析 bibtex 文件 但它基本上没有记录 我不知道如何对条目进行排序 pybtex 是可行的
  • 为什么 takewhile() 会跳过第一行?

    我有一个这样的文件 1 2 3 TAB 1 2 3 TAB 我想将 TAB 之间的行作为块读取 import itertools def block generator file with open file as lines for li
  • 使用 Numpy 与 einsum 和 tensordot 进行相同的操作

    假设我有两个 3D 数组A and B形状的 3 4 N and 4 3 N 我可以计算沿第三轴的切片之间的点积 with einsum np eisum ikl kjl gt ijl A B 是否可以执行相同的操作numpy tensor
  • Groupby Sum 忽略几列

    在此数据框中 我想按 位置 进行分组并获得 分数 的总和 但我不希望 纬度 经度 和 年份 在此过程中受到影响 sample pd DataFrame Location A B C A B C Year 2001 2002 2003 200
  • if(interactive()) 是否相当于 Python 中的“if __name__ == ”__main__“: main()”?

    我希望 R 脚本有一个 main 函数 可以在交互模式下执行 但在获取文件时不应执行 main 函数 已经有一个关于这个的问题了 https stackoverflow com questions 2968220 is there an r
  • 创建 Pyomo 约束的性能

    我正在用 pyomo 设置一个更大的能量优化问题 正如其他中提到的 设置花费了不合理的时间问题 https stackoverflow com questions 43413067 performance of pyomo to gener
  • python中的unicode错误[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 在下面的代码中我收到错误mailSe
  • 如何让 IPython 按类别组织制表符补全的可能性?

    当一个对象有数百个方法时 制表符补全很难使用 通常 有趣的方法是由被检查对象的类而不是其基类定义或重写的方法 如何让 IPython 对其制表符完成可能性进行分组 以便首先检查对象的类中定义的方法和属性 然后是基类中的方法和属性 看起来像是
  • Python UPnP/IGD 客户端实现?

    我正在寻找一个开源实现UPnP http elinux org UPnPPython 中的客户端 更具体地说是它的互联网网关设备 http en wikipedia org wiki Internet Gateway Device Prot
  • Python Sqlite3 获取 Sqlite 连接路径

    给定一个 sqlite3 连接对象 如何检索 sqlite3 文件的文件路径 The Python 连接对象 http github com python cpython blob master Modules sqlite connect
  • 将 pi 打印到小数点后几位

    w3resources 面临的挑战之一是将 pi 打印到小数点后 n 位 这是我的代码 from math import pi fraser str pi length of pi number of places raw input En
  • python webdriver_manager chrome 自定义配置文件

    如何使 webdriver manager chrome 使用自定义 chrome 用户配置文件 我知道对于 selenium webdriver 我可以这样指定 options Options options add argument f
  • 加入语音频道(discord.py)

    当我尝试让我的机器人加入我的语音频道时 出现以下错误 await client join voice channel voice channel 产生错误的行 Traceback most recent call last File usr

随机推荐

  • 将spawn()的stdout/stderr重定向到Ruby中的字符串

    我想使用spawn 对于多个并发子进程 在Ruby 中执行外部进程 并将stdout 或stderr 收集到一个字符串中 这与使用Python 的子进程Popen communicate 可以完成的方式类似 我尝试将 out err 重定向
  • ReadableByteChannelImpl 如何处理中断

    我正在尝试使用Channels newChannel包装输入流以支持中断 我看到了关于这是否有效的相互矛盾的信息 包括 ReadableByteChannelImpl 中的注释 Not really interruptible In Rea
  • 带有字符串键的 Lua 表不能使用数字索引?

    我是 Lua 的初学者 我认为使用字符串键将值推送到表中也会自动执行数字索引 但我认为我错了 我的代码 local t t name John t age 30 print Name t name nAge t age 虽然这段代码运行良好
  • 使用 css 3d 转换后如何修复 Safari 5 Mac OS X 中的脆文本?

    当应用 css 3d 变换的元素之后 相对和绝对定位的元素触发奇怪的行为时 我遇到了问题 为了解决这个问题 我必须设置背景颜色 但是如果我需要透明度怎么办 这是重现该错误的最低限度 http jsfiddle net 8VABq 3 htt
  • 如何获取 DD-MM-YYYY 格式的日期

    我正在使用 Parse 数据库 我存储日期格式 使用控制台时我得到这种格式 Tue Jul 18 2017 15 46 47 GMT 0100 CET 我想获得这种格式 18 07 2017 有什么想法请 可以使用以下代码来完成 let t
  • 抓取屏幕分辨率并根据大小显示结果

    我正在尝试根据屏幕分辨率大小显示数据库的结果 我上周发布了一个关于如何获取宽度的问题 并且能够在此处的一些想法的帮助下提出这个jquery 但无法完成第二个方面 即根据大小显示结果
  • 您正在使用遗留实现。请更新您的代码:使用createWrapper() 和wrapper.useWrappedStore()。 nextjs 还原?

    我在使用 redux 工具包和 next js 时遇到错误 我面临着这个遗留警告 You are using legacy implementaion Please update your code use createWrapper an
  • 填充 NaN 值

    我有一个数据框 TIMESTAMP P ACT KW PERIODE TARIF P SOUSCR 2016 01 01 00 00 00 116 HC 250 2016 01 01 00 10 00 121 HC 250 2016 01
  • doc.save() 与 jspdf 抛出错误

    当我尝试使用 jsPDF 的 save 函数时 它抛出以下错误 ReferenceError saveAs 未定义 我只是尝试一个非常简单的例子 var doc new jsPDF doc text 20 20 Hello world do
  • 什么是类型和效果系统?

    The 维基百科文章关于效果系统 http en wikipedia org wiki Effect system目前只是一个简短的存根 我有一段时间想知道什么是效果系统 除了类型系统之外 是否还有其他语言还具有效果系统 可能的 假设的 符
  • 在运行时枚举 .NET 程序集资源

    我有一个资源程序集 其中包含使用构建的图像文件Resource or Content建立行动 这使得可以使用 Uris 访问这些文件 但是我找不到枚举此类资源的方法 如果我将构建操作设置为嵌入式资源可以使用以下代码枚举文件 string r
  • 使用 GADT 在 Haskell 中重新创建 Lisp 的“apply”

    作为练习 我正在尝试重新创建 Lispapply在哈斯克尔 我不打算将其用于任何实际目的 我只是认为这是一个更好地熟悉 Haskell 类型系统和一般类型系统的好机会 所以我也不是在寻找其他人的实现 我的想法如下 我可以使用 GADT 来
  • 如何访问NTAG213 NFC交互计数器?

    我正在开发一个 Android 应用程序 我需要查找 NFC 标签被读取的次数 我已经浏览了 NFC 论坛上的文档 对于 NTAG213 有一个名为 MIRROR CONF 的字段 使用该字段可以确定交互计数器是否启用 禁用以及计数器的值
  • 如何用java播放.wav文件

    我正在尝试用 Java 播放 wav 文件 我希望它执行以下操作 按下按钮时 会发出短促的蜂鸣声 我用谷歌搜索了一下 但大部分代码都不起作用 有人能给我一个简单的代码片段来播放 wav 文件吗 最后我设法做到了以下几点并且效果很好 impo
  • p 与 ol 或 ul 的表单样式

    通常我使用无序列表标签来设计表单 例如 fieldset ul li li ul fieldset
  • 在 Android 上使用 Jackson 库解析大型 JSON 时出现内存不足错误

    我正在使用 Jackson 库来解析来自服务器的大型 JSON 响应 json 的大小约为 7 8 MB 我在这段代码上遇到了 outOfMemoryError ObjectMapper mapper new ObjectMapper Js
  • Gitlab docker executor - 在 before_script 之后缓存图像

    In gitlab ci有一个选项 gitlab ci yml http doc gitlab com ce ci yaml README html文件在任何实际脚本运行之前执行命令 称为before script gitlab ci ym
  • JavaFX 应用程序抛出 NullPointerExceptions 但仍然运行

    我运行的每个 JavaFX 应用程序都会抛出两个 NullPointerException 它们不会阻止甚至影响项目的执行 并且只有在调试模式下运行应用程序时我才能看到它们 我什至遇到了来自 Oracle 的 HelloWorld 示例和这
  • RavenDB 在模型命名空间更改后抛出转换异常

    在数据库中 我已经存储了数百个文档 现在系统架构已经改变 并且 除其他外 模型被迁移到不同的命名空间 在不同的程序集中 下面显示了示例文档的元数据 以及我用来获取此类文档的代码 var configuration documentSessi
  • 如何在 FastAPI 中自定义特定路由的错误响应?

    我想做一个HTTPFastAPI 中需要特定的端点Header 产生一个定制的response代码时Header不存在 并且显示Header as required在 FastAPI 生成的 OpenAPI 文档中 例如 如果我将此端点设置