fastAPI(8)--错误处理

在许多情况下,您需要将错误通知给使用API​​的客户端。

该客户端可以是带有前端的浏览器,来自其他人的代码,IoT设备等。

您可能需要告诉客户:

  • 客户端没有足够的权限进行该操作。
  • 客户端无权访问该资源。
  • 客户端尝试访问的项目不存在。
  • 等等

在这些情况下,通常会返回400(从400到499)范围内的HTTP状态代码

这类似于200个HTTP状态代码(从200到299)。这些“ 200”状态代码意味着请求中某种程度上存在“成功”。

400范围内的状态代码表示来自客户端的错误。

#!/usr/bin/env python
# encoding: utf-8
from fastapi import FastAPI, HTTPException
import uvicorn

app = 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]}


if __name__ == '__main__':
    uvicorn.run(app=app, host='127.0.0.1', port=8000)

2.添加自定义头信息

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

3. 自定义异常处理

借助 the same exception utilities from Starlette,可以添加自定义异常处理器。
假设有个自定义异常 UnicornException,想在全局范围内处理这个异常。
借助 @app.exception_handler(),就可以实现。

#!/usr/bin/env python
# encoding: utf-8

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import uvicorn

app = FastAPI()

class UnicornException(Exception):
    def __init__(self, name: str):
        self.name = name

@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"}
    )

@app.get('/unicorns/{name}')
async def read_unicorn(name: str):
    if name == 'yolo':
        raise UnicornException(name=name)
    return {"unicorn_name": name}

if __name__ == '__main__':
    uvicorn.run(app=app, host='127.0.0.1', port=8000)

这里如果请求 /unicorns/yolo,路径操作函数就会抛出异常 UnicornException,这个异常会被自定义的异常处理器unicorn_exception_handler捕获到。

运行:http://127.0.0.1:8000/unicorns/yolo

返回结果:{"message":"Oops! yolo did something"}

4. 重写错误处理

FastAPI有一些缺省的异常处理器。当抛出HTTPException异常或者当请求有非法数据的时候,处理器负责返回默认的JSON结果。可以重写这些异常处理。

4.1  重写RequestValidationError

当一个请求包含非法数据的时候,FastAPI内部会抛出RequestValidationError异常,并且有默认的异常处理器来处理。可以用 @app.exception_handler(RequestValidationError) 来重写这个异常处理器。

#!/usr/bin/env python
# encoding: utf-8

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
import uvicorn

app = 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}

if __name__ == '__main__':
    uvicorn.run(app=app, host='127.0.0.1', port=8000)

如果请求 http://127.0.0.1:8000/items/foo,那么返回结果就不是默认的:

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

而是:

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

4.2  重写HTTPException

以上方代码为例:

如果请求 http://127.0.0.1:8000/items/3,这时候返回的错误信息为:Nope! I don't like 3.

如果没有自定义异常处理器http_exception_handler,返回的错误信息为:{"detail":"Nope! I don't like 3."}

5. 重用缺省异常处理器

#!/usr/bin/env python
# encoding: utf-8

from fastapi import FastAPI, HTTPException
import uvicorn
from fastapi.exception_handlers import (http_exception_handler, request_validation_exception_handler,)
from fastapi.exceptions import RequestValidationError, HTTPException as StarletteHTTPException

app = 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": item_id}


if __name__ == '__main__':
    uvicorn.run(app=app, host='127.0.0.1', port=8000)

在示例中,在抛出异常之前添加了一条日志输出。可以根据业务需求灵活的重用缺省异常处理器。

 

FastAPI HTTPException 对比 Starlette HTTPException
FastAPI HTTPException 继承自 Starlette的HTTPException。
唯一的区别是,FastAPI HTTPException允许你在response添加头信息。主要在内部用于OAuth 2.0以及一些安全相关的功能。
因此,通常在代码中抛出FastAPI HTTPException异常。
但是,当注册异常处理器的时候,应该注册为Starlette HTTPException。
这样,当Starlette的内部代码或者Starlette扩展插件抛出Starlette HTTPException时,处理器才能正常捕获和处理这个异常。
为了使两个都具有HTTPException相同的代码,Starlette的异常被重命名为
from starlette.exceptions import HTTPException as StarletteHTTPException

 

上一篇:解决phpqrcode生成二维码出现乱码


下一篇:FastAPI(27)- Handling Errors 处理错误