FASTAPI系列 20-异常处理器exception_handler
文章目录
- FASTAPI系列 20-异常处理器exception_handler
- 前言
- 一、HTTPException 异常?
- 二、覆盖默认的HTTPException 异常
- 三、覆盖请求验证异常
- RequestValidationError 源码分析
- 总结
- 更多内容,请关注公众号
前言
通常我们可以通过 raise 抛出一个 HTTPException 异常,请求参数不合法会抛出RequestValidationError 异常,这是最常见的2种异常。
一、HTTPException 异常?
向客户端返回 HTTP 错误响应,可以使用 raise 触发 HTTPException。
from fastapi import FastAPI, HTTPExceptionapp = FastAPI()@app.get("/path/{name}")
async def get_name(name: str): if name != "Teacher Li": raise HTTPException(404, detail=f"name: {name} not found") return {"name": name}
二、覆盖默认的HTTPException 异常
查看HTTPException 异常相关源码
from starlette.exceptions import HTTPException as StarletteHTTPException class HTTPException(StarletteHTTPException): def __init__( self, status_code: int, detail: Any = None, headers: Optional[Dict[str, Any]] = None, ) -> None: super().__init__(status_code=status_code, detail=detail, headers=headers)
HTTPException 异常是继承的 starlette 包里面的 HTTPException覆盖默认异常处理器时需要导入 from starlette.exceptions import HTTPException as StarletteHTTPException,并用 @app.excption_handler(StarletteHTTPException) 装饰异常处理器。
from fastapi import FastAPI, Request
from fastapi.exceptions import HTTPException
from fastapi.responses import PlainTextResponse, JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException app = FastAPI() # # 捕获 HTTPException 异常
@app.exception_handler(StarletteHTTPException)
def http_error(request, exc): print(exc.status_code) print(exc.detail) # return JSONResponse({'error_msg': exc.detail}, status_code=exc.status_code) return PlainTextResponse(content=exc.detail, status_code=exc.status_code) @app.get("/path/{name}")
async def get_name(name: str): if name != "Teacher Li": raise HTTPException(404, detail=f"name: {name} not found") return {"name": name}
三、覆盖请求验证异常
请求中包含无效数据时,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(RequestValidationError)
async def validation_exception_handler(request, exc):return PlainTextResponse(str(exc), status_code=400)@app.get("/user/{user_id}")
async def get_user(user_id: int):if user_id == 3:raise HTTPException(status_code=418, detail="Nope! I don't like 3.")return {"user_id": user_id}
RequestValidationError 源码分析
RequestValidationError 相关源码
class RequestValidationError(ValidationError): def __init__(self, errors: Sequence[ErrorList], *, body: Any = None) -> None: self.body = body super().__init__(errors, RequestErrorModel)
使用示例
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): print(exc.json()) print(exc.errors()) print(exc.body) # 请求body return JSONResponse( status_code=400, content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}), ) class Book(BaseModel): name: str price: float@app.post("/books/")
async def create_book(book: Book): return book
总结
FastAPI 调用的就是 RequestValidationError 类,因此,如果在 response_model 中使用 Pydantic 模型,且数据有错误时,在日志中就会看到这个错误。但客户端或用户看不到这个错误。反之,客户端接收到的是 HTTP 状态码为 500 的「内部服务器错误」。这是因为在_响应_或代码(不是在客户端的请求里)中出现的 Pydantic ValidationError 是代码的 bug。修复错误时,客户端或用户不能访问错误的内部信息,否则会造成安全隐患。