如何在FastAPI中自定义错误响应?

4
我有以下的FastAPI后端:
from fastapi import FastAPI

app = FastAPI

class Demo(BaseModel):
    content: str = None
    
@app.post("/demo")
async def demoFunc(d:Demo):
    return d.content

问题在于当我向这个API发送带有额外数据的请求时,会出现以下情况:
data = {"content":"some text here"}aaaa

或者

data = {"content":"some text here"aaaaaa}

resp = requests.post(url, json=data)

如果data = {"content":"some text here"}aaaa,则会出现状态码为422不可处理的实体的错误,返回字段中包含实际值("some text here")和额外数据("aaaaa")。

{
  "detail": [
    {
      "loc": [
        "body",
        47
      ],
      "msg": "Extra data: line 4 column 2 (char 47)",
      "type": "value_error.jsondecode",
      "ctx": {
        "msg": "Extra data",
        "doc": "{\n  \"content\": \"some text here\"}aaaaa",
        "pos": 47,
        "lineno": 4,
        "colno": 2
      }
    }
  ]
}

我尝试将app=FastAPI()这行代码放在try-catch块中,但是它并没有起作用。有没有办法处理这个问题并自定义响应而不是使用上述的自动响应呢?类似于下面这样:

{"error": {"message": "Invalid JSON body"},
                         "status": 0}

你期望的结果是什么?这并不是有效的 JSON,那你想如何解析它呢? - MatsLindh
我想展示自定义的响应,而不是来自API本身的自动响应。 - draaken
你看过 https://fastapi.tiangolo.com/tutorial/handling-errors/ 吗?它告诉你如何覆盖特定的错误并自己处理响应。 - MatsLindh
我看到了,但是没有正确理解。但现在问题已经解决了,感谢Chris,也谢谢@MatsLindh。 - draaken
2个回答

5

您正在传递无效的JSON,因此服务器正确地响应了422 Unprocessable Entity错误。您的测试客户端不应该能够运行,而不抛出invalid syntax错误。所以,我猜测您是通过Swagger UI提供的交互式自动文档/docs发送请求,并收到了相关的422错误。

如果您实际想要处理错误,以便自定义错误或其他内容,您可以覆盖请求验证异常处理程序,如文档中所述(请查看讨论,以及此答案此答案,演示了如何仅为特定路由自定义RequestValidationError)。

工作示例:

from fastapi import FastAPI, Body, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = 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(),  # optionally include the errors
                "body": exc.body,
                 "custom msg": {"Your error message"}}),
    )

class Demo(BaseModel):
    content: str = None

@app.post("/demo")
async def some_func(d: Demo):
    return d.content

或者,您也可以返回一个带有自定义消息的PlainTextResponse

from fastapi.responses import PlainTextResponse

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    return PlainTextResponse(str(exc), status_code=422) 

我知道这是无效的JSON数据,这正是重点所在。实际上,这是一种跨站点脚本测试的情况,其中测试人员使用不同的方法在请求体中获取信息。例如,一个人可以在请求体中放置一个脚本,如下所示:{"content":"some text"}<script>/*+Bad+script+here...+*/</script> 因此,我知道API将抛出422错误,但它也会返回请求体本身,我不想显示它。这就是我问如何捕获并显示自己的错误消息的原因。 - draaken
我使用JSONResponse来显示类似以下的自己的信息:return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content={"error": {"message": "无效的JSON格式"}, "status": 0} ) - draaken
Chris,我试了你的代码,但还是一样的。{ "detail": [ { "loc": [ "body", 45 ], "msg": "期望','分隔符:第3行第24列(字符45)", "type": "value_error.jsondecode", "ctx": { "msg": "期望','分隔符", "doc": "{\n \"gender\": \"male\",\n \"language\": \"English\"aaaaa\n}", "pos": 45, "lineno": 3, "colno": 24 } } ], "custom msg": { "error": { "message": "无效的json格式" }, "status": 0 } } - draaken
1
没关系,我修改了提供的答案,现在它可以工作了。谢谢 @Chris - draaken

0
当您发送带有请求体的请求时,FastAPI 将尝试使用内置的 json 模块中的 `loads` 函数对请求体进行反序列化,而您得到的错误是由该函数引发的。
因此,如果您想自定义响应,您必须以与 Chris 所说的相同方式创建一个异常处理程序并进行处理。类似于以下内容:
from fastapi import status
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError


app = FastAPI()

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
   errors = exc.errors()
   response = []
   for error in errors:
      if error['type'] == 'value_error.jsondecode':
         response.append({'error': {'message': 'Invalid JSON body'}, 'status': 0})
      else:
      response.append(error)
   return JSONResponse(content=response, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接