items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
"""
the in operator for dictionaries checks if the given key exists in the dictionary
in items is slightly more efficient because it directly uses the dictionary's hash table for lookup
the items.keys() method is more useful when you need to perform operations on the keys themselves,
such as set operations or when you need to pass the keys to another function
common_keys = set(items.keys()) & set(other_dict.keys())
"""
if item_id not in items:
"""
The situation with HTTP headers and character encoding is a bit nuanced:
Header Names: These should always be ASCII characters.
This is a strict requirement in the HTTP specification.
Header Values: The HTTP/1.1 specification (RFC 7230) states that header field values may contain
any character from the ASCII character set, except for a few specific control characters.
However, non-ASCII characters (like Chinese characters) are not directly supported in header values.
Practical Implementation: Many modern web servers and clients can handle non-ASCII characters in header values,
but this is not guaranteed to work universally.
It may cause issues with some servers, proxies, or other intermediaries.
Best Practices: To ensure maximum compatibility and adherence to standards,
it's recommended to restrict header values to ASCII characters.
When you need to include non-ASCII content,
you should encode it (using methods like URL encoding, Base64, etc.) as we discussed earlier.
"""
raise HTTPException(status_code = 404,
detail = "Item not found",
headers = {
"my-header": quote("this is my header error info 中文信息"),
"Content-Type": "application/json; charset=utf-8"
})
return {"item": items[item_id]}
通过raise HTTPException抛出一个异常,这个异常不仅设置了HTTP状态码为404,表示“未找到”,还详细描述了错误信息“Item not found”。此外,我们还在响应头中设置了自定义的my-header和Content-Type,后者指定了响应内容的类型和字符集。
在设置HTTP头时,我们需要考虑字符编码的问题。根据HTTP规范,头部名称必须是ASCII字符,而头部值可以包含ASCII字符集中的任何字符,除了一些特定的控制字符。虽然现代的Web服务器和客户端可以处理非ASCII字符,但这并不是普遍支持的,可能会导致与某些服务器或代理的兼容性问题。因此,为了确保最大的兼容性和遵守标准,建议将头部值限制为ASCII字符。如果需要包含非ASCII内容,应该进行编码,比如使用URL编码或Base64。
class MyException(Exception):
def __init__(self):
self.occurred_time = datetime.datetime.now().isoformat()
self.uuid = uuid.uuid4().hex
@app.exception_handler(MyException)
def my_exception_handler(request: Request, exc: MyException):
return JSONResponse(
status_code = http.HTTPStatus.BAD_REQUEST,
content = {
'exception_occurred_time': exc.occurred_time,
'exception_uuid': exc.uuid,
'method': request.method,
'url': f'{request.url.scheme}://{request.url.hostname}:{request.url.port}{request.url.path}',
'headers': dict(request.headers.items()),
})
@app.post('/raise_my_exception/{test_id}')
def raise_my_exception(test_id: str, req: Request):
if test_id == 'raise':
raise MyException()
else:
return JSONResponse(
status_code = http.HTTPStatus.OK,
content = {
'url': f'{req.url.scheme}://{req.url.hostname}:{req.url.port}{req.url.path}',
'method': req.method,
'headers': dict(req.headers.items()),
}
)
在构建Web应用时,异常处理是一个关键环节,它不仅关系到应用的稳定性,也直接影响到用户体验。FastAPI作为一个现代、快速(高性能)的Web框架,提供了强大的异常处理机制,允许开发者自定义异常,从而更精细地控制错误响应。本文将通过一个实际的例子,展示如何在FastAPI中实现自定义异常处理。
首先,我们定义了一个名为MyException
的自定义异常类,它继承自Python的基类Exception
。在初始化方法__init__
中,我们记录了异常发生的时间(使用ISO格式),并为每个异常实例生成了一个唯一的UUID。这两个属性将用于后续的错误响应中,以提供详细的错误信息。
class MyException(Exception):
def __init__(self):
self.occurred_time = datetime.datetime.now().isoformat()
self.uuid = uuid.uuid4().hex
接下来,我们使用@app.exception_handler
装饰器来定义一个异常处理器。这个处理器专门用来处理MyException
类型的异常。当MyException
被抛出时,FastAPI将调用这个处理器,并传入请求对象和异常对象。处理器返回一个JSONResponse
对象,其中包含了异常发生的时间、UUID、请求方法、URL以及请求头等信息。
@app.exception_handler(MyException)
def my_exception_handler(request: Request, exc: MyException):
return JSONResponse(
status_code = http.HTTPStatus.BAD_REQUEST,
content = {
'exception_occurred_time': exc.occurred_time,
'exception_uuid': exc.uuid,
'method': request.method,
'url': f'{request.url.scheme}://{request.url.hostname}:{request.url.port}{request.url.path}',
'headers': dict(request.headers.items()),
}
)
在实际的路由处理函数中,我们可以根据业务逻辑抛出MyException
。例如,当POST请求的test_id
参数为'raise'
时,我们抛出MyException
,触发自定义的异常处理流程。
@app.post('/raise_my_exception/{test_id}')
def raise_my_exception(test_id: str, req: Request):
if test_id == 'raise':
raise MyException()
else:
return JSONResponse(
status_code = http.HTTPStatus.OK,
content = {
'url': f'{req.url.scheme}://{req.url.hostname}:{req.url.port}{req.url.path}',
'method': req.method,
'headers': dict(req.headers.items()),
}
)
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
"""
HTTPException is a subclass of StarletteHTTPException
so when you raise an HTTPException
it's caught by the StarletteHTTPException handler
"""
return PlainTextResponse(str(f'http_exception_handler, '
f'this is my http exception info:{exc.detail}, '
f'req headers: {request.headers}'
f'req url: {request.url.path}'),
status_code = exc.status_code)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""
RequestValidationError is a specific exception raised by Pydantic when request validation fails
It's caught by the RequestValidationError handler
the custom ValidationException doesn't work with the RequestValidationError handler (they're unrelated)
when you raise an exception
FastAPI looks for the most specific matching exception handler
If it doesn't find one
it moves up the exception hierarchy
until it finds a suitable handler
or reaches the default exception handling behavior.
FastAPI doesn't have a built-in exception class specifically named "ValidationException".
Instead, it primarily uses the following exception classes for validation-related issues:
RequestValidationError: This is raised when the incoming request data fails validation against the defined model or parameters.
It's part of FastAPI and is raised automatically when request data doesn't match the expected schema.
ValidationError: This is a Pydantic exception, which FastAPI uses internally.
It's raised when data fails validation against a Pydantic model.
"""
return PlainTextResponse(str(f'validation_exception_handler, '
f'this is my validation exception info: {exc.errors()},'
f'req headers: {request.headers},'
f'req url: {request.url.path},'
f'body: {exc.body}'),
status_code = 400)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 0:
raise HTTPException(status_code = 418, detail = f'item_id is {item_id}')
if item_id == 1:
"""
curl -X 'GET' 'http://127.0.0.1:18081/items/abc' -H 'accept: application/json'
"""
raise RequestValidationError(errors = ['ABC', 'DEF'])
return {"item_id": item_id}
class Item(BaseModel):
title: str
size: int
@app.post("/items/")
async def create_item(item: Item):
return item
在FastAPI中,HTTPException
是StarletteHTTPException
的子类,这意味着当你抛出一个HTTPException
时,它会被StarletteHTTPException
的处理程序捕获。这种机制允许开发者为特定类型的HTTP异常定义统一的处理逻辑。
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
return PlainTextResponse(
str(f'http_exception_handler, this is my http exception info:{exc.detail}, req headers: {request.headers} req url: {request.url.path}'),
status_code=exc.status_code
)
FastAPI使用RequestValidationError
来处理请求验证失败的情况,这是Pydantic在请求数据不符合预期模式时自动抛出的异常。开发者可以为这个异常定义一个处理器,以提供更详细的错误信息。
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return PlainTextResponse(
str(f'validation_exception_handler, this is my validation exception info: {exc.errors()}, req headers: {request.headers}, req url: {request.url.path}, body: {exc.body}'),
status_code=400
)
在实际的路由处理中,开发者可以根据业务逻辑抛出不同的异常。例如,如果item_id
为0,可以抛出一个HTTPException
;如果item_id
为1,可以抛出一个RequestValidationError
。
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 0:
raise HTTPException(status_code=418, detail=f'item_id is {item_id}')
if item_id == 1:
raise RequestValidationError(errors=['ABC', 'DEF'])
return {"item_id": item_id}
FastAPI在处理异常时,会从最具体的异常类型开始查找匹配的处理程序。如果没有找到,它会沿着异常继承体系向上查找,直到找到一个合适的处理器或到达默认的异常处理行为。这种机制使得FastAPI的异常处理既灵活又强大。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。