在许多情况下,您需要将错误通知给使用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