python:Fastapi - 异常处理、路径配置及JSON编码器
简单絮叨一些
上篇文章主要唠了表单和文件上传等功能,这次主要是唠下异常处理、路径操作配置和JSON兼容编码器等。异常处理就是针对
某些情况下,需要向客户端返回错误提示。路径操作配置就是路径操作装饰器支持多种配置参数。JSON兼容器是在某些情况
下,您可能需要将数据类型转换为与 JSON 兼容的类型等,然后存储起来。
处理错误
使用HTTPException
某些情况下,需要向客户端返回错误提示。
需要向客户端返回错误提示的场景主要如下:
- 客户端没有执行操作的权限
- 客户端没有访问资源的权限
- 客户端要访问的项目不存在
- 等等 …
以上情况通常返回4xx的HTTP状态码,4xx的状态码通常是指客户端发生的错误。
向客户端返回 HTTP 错误响应,可以使用 HTTPException,这也是fastapi默认。
from fastapi import FastAPI
from fastapi import HTTPException
app = FastAPI()
items = {
"foo": "333_2222_666"
}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
"""
初次使用HTTPException
:param item_id:
:return:
"""
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
注释信息:
raise HTTPException(status_code=404, detail="Item not found")是触发异常并抛出detail="Item not found"参数detail传递任何能转换为JSON的值,不仅限于str,还支持传递dict、list等数据结构
注意点:
Python异常,不能return,只能raise。使用
return虽然不会报错,但牵扯到多层调用时,异常信息不一定会全部抛出,而raise是专门抛异常的关键字。
启动服务:
PS E:\git_code\python-code\fastapiProject> uvicorn handle_main:app --reload
请求接口:
GET http://127.0.0.1:8000/items/bee
请求结果:
{
"detail": "Item not found"
}
因为bee不在items字典中,故触发异常并抛出异常信息。
添加自定义响应头
为HTTP错误添加自定义信息头,暂时使用场景还没实操,可先了解下即可。
from fastapi import FastAPI
from fastapi import HTTPException
app = FastAPI()
@app.get("/items-header/{item_id}")
async def read_item_header(item_id: str):
"""
自定义响应头
:param item_id:
:return:
"""
if item_id not in items:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"H-Error": "This is an error message."}
)
return {"item": items[item_id]}
注释信息:
headers={"H-Error": "This is an error message."}添加自定义响应头
自定义异常处理器
自定义异常顾名思义就是自己去写一个异常处理器,不过也是继承python中异常的基类Exception。
from fastapi import FastAPI
from fastapi import Request
from fastapi.responses import JSONResponse
class UnicornException(Exception):
def __init__(self, name):
self.name = name
app = FastAPI()
@app.exception_handler(UnicornException)
async def unicorn_exception_handle(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"{exc.name} 错误..."}
)
@app.get("/unicorn/{name}")
async def read_unicorn(name: str):
"""
引用自定义异常UnicornException
:param name:
:return:
"""
if name == "lifeng":
raise UnicornException(name=name)
return {"unicorn_name": name}
注释信息:
class UnicornException(Exception):定义一个继承Exception的类,并顶一个name属性@app.exception_handler(UnicornException)是添加自定义异常控制器
启动服务:
PS E:\git_code\python-code\fastapiProject> uvicorn handle_main:app --reload
请求接口:
GET http://127.0.0.1:8000/unicorn/lifeng
请求 unicorn/lifeng 时,路径操作会触发 UnicornException。
因该异常将会被 unicorn_exception_handler 处理。
接收到的错误信息清晰明了,HTTP 状态码为 418,请求结果的JSON内容如下:
{
"message": "lifeng 错误..."
}
覆盖默认异常处理器
触发 HTTPException 或请求无效数据时,这些处理器返回默认的 JSON 响应结果。
不过,也可以使用自定义处理器覆盖默认异常处理器。
1 - 覆盖请求验证异常
覆盖默认异常处理器时需要导入 RequestValidationError,并用 @app.excption_handler(RequestValidationError) 装饰异常处理器。
这样,异常处理器就可以接收 Request 与异常。
from fastapi import FastAPI
from fastapi import HTTPException
from fastapi.responses import PlainTextResponse
from fastapi.exceptions import RequestValidationError
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return PlainTextResponse(str(exc), status_code=400)
@app.get("/cover/{cover_id}")
async def read_cover(cover_id: int):
if cover_id == 3:
raise HTTPException(status_code=418, detail="hahahha")
return {"cover_id": cover_id}
注释信息:
@app.exception_handler(RequestValidationError)是添加自定义异常控制器return PlainTextResponse(str(exc), status_code=400)是返回字符串类型的响应数据
启动服务:
PS E:\git_code\python-code\fastapiProject> uvicorn handle_main:app --reload
请求接口:
GET http://127.0.0.1:8000/cover/ttt
请求结果:
文本格式的错误信息
1 validation error for Request
path -> cover_id
value is not a valid integer (type=type_error.integer)
2 - 覆盖HTTPException错误处理器
同理,也可以覆盖 HTTPException 处理器。
例如,只为错误返回纯文本响应,而不是返回 JSON 格式的内容:
from fastapi import FastAPI
from fastapi import HTTPException
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
@app.get("/cover/{cover_id}")
async def read_cover(cover_id: int):
if cover_id == 3:
raise HTTPException(status_code=418, detail="hahahha")
return {"cover_id": cover_id}
注释信息:
from starlette.exceptions import HTTPException as StarletteHTTPException引包后起别名@app.exception_handler(StarletteHTTPException)是添加自定义异常控制器return PlainTextResponse(str(exc), status_code=400)是返回字符串类型的响应数据
注意点:
from fastapi import HTTPException
实际就是继承的
from starlette.exceptions import HTTPException as StarletteHTTPException
所以http_exception_handler实际就是重写了StarletteHTTPException基类,自然就改变了默认的HTTPException
启动服务:
PS E:\git_code\python-code\fastapiProject> uvicorn handle_main:app --reload
请求接口:
GET http://127.0.0.1:8000/cover/3
请求结果:
文本格式的错误信息
hahahha
使用RequestValidationError的请求体
使用RequestValidationError 包含其接收到的无效数据请求的 body 。
from fastapi import FastAPI
from fastapi import Request
from fastapi import status
from pydantic import BaseModel
from fastapi import HTTPException
from fastapi.encoders import jsonable_encoder
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):
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
)
class Item(BaseModel):
title: str
size: int
@app.post("/items/")
async def create_item(item: Item):
return item
启动服务:
PS E:\git_code\python-code\fastapiProject> uvicorn handle_main:app --reload
请求接口:
POST http://127.0.0.1:8000/items
请求参数:
{
"title": "222",
"size": "####"
}
请求结果:
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "222",
"size": "####"
}
}
请求结果最后的body字典就是说明数据是无效的。
复用 FastAPI 异常处理器
FastAPI 支持先对异常进行某些处理,然后再使用 FastAPI 中处理该异常的默认异常处理器。
从 fastapi.exception_handlers 中导入要复用的默认异常处理器:
from fastapi import FastAPI
from fastapi import HTTPException
from fastapi.exception_handlers import (
http_exception_handler,
request_validation_exception_handler
)
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
print(f"333: {repr(request)}, 444: {repr(exc)}")
if exc.status_code == 418:
print(exc.detail)
return await http_exception_handler(request, exc)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
print(f"555: {repr(request)}, 666: {repr(exc)}")
return await request_validation_exception_handler(request, exc)
@app.get("/cover/{cover_id}")
async def read_cover(cover_id: int):
if cover_id == 3:
raise HTTPException(status_code=418, detail="hahahha")
return {"cover_id": cover_id}
上述代码中exc.status_code是后台读出来的,这样我们就可以对异常处理后再返回了,repr是可以看到源数据。
INFO: 127.0.0.1:51952 - "GET /cover/3 HTTP/1.1" 418 I'm a Teapot
333: <starlette.requests.Request object at 0x00000160206E2560>, 444: HTTPException(status_code=418, detail='hahahha')
hahahha
INFO: 127.0.0.1:52027 - "GET /cover/3 HTTP/1.1" 418 I'm a Teapot
555: <starlette.requests.Request object at 0x00000160206E2AD0>, 666: RequestValidationError(model='Request', errors=[{'loc': ('path', 'cover_id'), 'msg': 'value is not a valid integer', 'type': 'type_error.in
teger'}])
INFO: 127.0.0.1:52032 - "GET /cover/tt HTTP/1.1" 422 Unprocessable Entity
路径操作配置
status_code用于定义路径操作响应中的HTTP状态码。可以直接传递int代码, 比如404。如果记不住数字码的涵义,也可以用status的快捷常量。tags参数的值是由str组成的list(一般只有一个 str ),tags用于为路径操作添加标签路径装饰器还支持
summary和description这两个参数,summary是文档中的一个标题,description是文档中接口的描述信息response_description参数用于定义响应的描述说明(注意,response_description只用于描述响应,description一般则用于描述路径操作)。deprecated参数可以把路径操作标记为弃用,无需直接删除,文档中显示为置灰
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
from fastapi import status
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
@app.post("/items/", response_model=Item,
tags=["itemss"], status_code=201 or status.HTTP_201_CREATED,
summary="Testing",
description="描述信息",
response_description="参数用于定义响应的描述说明",
deprecated=True
)
def read_item(item: Item):
"""
状态码可以是纯数字或者快捷常量都可以
:param item:
:return:
"""
return item
启动服务:
PS E:\git_code\python-code\fastapiProject> uvicorn config_main:app --reload
浏览器请求接口:
http://127.0.0.1:8000/docs
即可看到对应参数对应的显示效果。
JSON兼容编码器
在某些情况下,您可能需要将数据类型转换为与 JSON 兼容的类型(如dict、list等)。
然后需要将其存储在数据库中,为此,FastAPI提供了一个jsonable_encoder()功能。
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
from fastapi import status
from fastapi.encoders import jsonable_encoder
results_dict = {}
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
app = FastAPI()
@app.put("/items/{item_id}", status_code=201 or status.HTTP_201_CREATED)
def read_item(item_id, item: Item):
"""
jsonable_encoder实际上是FastAPI内部用来转换数据的。
:param item_id:
:param item:
:return:
"""
data = jsonable_encoder(item)
results_dict[item_id] = data
return results_dict
启动服务:
PS E:\git_code\python-code\fastapiProject> uvicorn config_main:app --reload
请求接口:
PUT http://127.0.0.1:8000/items/foo
请求参数:
{
"name": "lifeng",
"price": 3.3
}
请求参数:
{
"foo": {
"name": "lifeng",
"description": null,
"price": 3.3
}
}
jsonable_encoder实际上是FastAPI内部用来转换数据的。但它在许多其他场景中很有用。
今天先聊到这里吧,以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,直接私信留言会及时修正发布;非常期待你的一键 3 连【 点赞、收藏、分享 】哟,谢谢!
未完成,待续……
一直在努力,希望你也是!
微信搜索公众号:就用python

浙公网安备 33010602011771号