简单絮叨一些
上篇文章主要唠了表单和文件上传等功能,这次主要是唠下异常处理、路径操作配置和JSON兼容编码器等。异常处理就是针对
某些情况下,需要向客户端返回错误提示。路径操作配置就是路径操作装饰器支持多种配置参数。JSON兼容器是在某些情况
下,您可能需要将数据类型转换为与 JSON 兼容的类型等,然后存储起来。
处理错误
使用HTTPException
某些情况下,需要向客户端返回错误提示。
需要向客户端返回错误提示的场景主要如下:
- 客户端没有执行操作的权限
- 客户端没有访问资源的权限
- 客户端要访问的项目不存在
- 等等 …
以上情况通常返回4xx的HTTP状态码,4xx的状态码通常是指客户端发生的错误。
向客户端返回 HTTP 错误响应,可以使用 HTTPException,这也是fastapi默认。
from fastapi import FastAPIfrom fastapi import HTTPExceptionapp = 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 FastAPIfrom fastapi import HTTPExceptionapp = 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 FastAPIfrom fastapi import Requestfrom fastapi.responses import JSONResponseclass UnicornException(Exception): def __init__(self, name): self.name = nameapp = 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 FastAPIfrom fastapi import HTTPExceptionfrom fastapi.responses import PlainTextResponsefrom fastapi.exceptions import RequestValidationErrorapp = 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 Requestpath -> cover_id value is not a valid integer (type=type_error.integer)
2 - 覆盖HTTPException错误处理器
同理,也可以覆盖 HTTPException 处理器。
例如,只为错误返回纯文本响应,而不是返回 JSON 格式的内容:
from fastapi import FastAPIfrom fastapi import HTTPExceptionfrom fastapi.responses import PlainTextResponsefrom starlette.exceptions import HTTPException as StarletteHTTPExceptionapp = 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 FastAPIfrom fastapi import Requestfrom fastapi import statusfrom pydantic import BaseModelfrom fastapi import HTTPExceptionfrom fastapi.encoders import jsonable_encoderfrom fastapi.responses import JSONResponsefrom fastapi.exceptions import RequestValidationErrorapp = 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 FastAPIfrom fastapi import HTTPExceptionfrom fastapi.exception_handlers import ( http_exception_handler, request_validation_exception_handler)from fastapi.exceptions import RequestValidationErrorfrom starlette.exceptions import HTTPException as StarletteHTTPExceptionapp = 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 Teapot333: <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 Teapot555: <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.integer'}])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 FastAPIfrom pydantic import BaseModelfrom typing import Optionalfrom fastapi import statusapp = 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 FastAPIfrom pydantic import BaseModelfrom typing import Optionalfrom fastapi import statusfrom fastapi.encoders import jsonable_encoderresults_dict = {}class Item(BaseModel): name: str description: Optional[str] = None price: floatapp = 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
版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除