持续创作,加快成长!这是我参加「日新方案 6 月更文应战」的第30天,点击查看活动概况
我们好~我是小方,欢迎我们关注笋货测试笔记体完记住俾个like呀
踩坑-脱坑
组内维护着一个造数渠道,用的是fastapi为web框架,为了体会更好,做了一些自定义反常处理,手动捕获反常和主动捕获过错,捕获的信息经过日志和企微机器人发送反常信息或者报错信息,但是主动捕获过错,无法捕获body,这让我很头疼!!!具体可看TesterHome原贴:testerhome.com/topics/2890…
帖子放上去之后,都没能处理;问了公司的测开,涉及到知识盲区,也没能处理!之前这个坑,也就暂时放下来了~
File "C:\Users\junjie\AppData\Local\Programs\Python\Python37\lib\site-packages\starlette\requests.py", line 167, in empty_receive
raise RuntimeError("Receive channel has not been made available")
RuntimeError: Receive channel has not been made available
从头跳坑
最近在看无敌哥的pity渠道,用的是异步处理,看看香不香!!!拉了代码下来跑,跑到运转用例时,就报500过错,Response都没有回来,发现无敌哥把主动捕获过错的处理注释掉了,看到这段代码,想起之前的坑,赶紧敲击一下无敌哥,无敌哥马上去搜材料,真不愧是无敌卷魔,肝帝!
填坑
无敌哥刮遍了整个搜索引擎和fastapi的issues,找到了两个处理方案,无敌卷魔牛逼!
自定义中间件-Middleware
在FastAPI使用中使用中间件。
中间件实际上是一个函数,在每个request处理之前被调用,同时又在每个response回来之前被调用。
- 首先接收拜访过来的request。
- 然后针对request或其他功用执行自定义逻辑。
- 传递request给使用程序继续处理。
- 接收使用所发生的response。
- 然后针对response或其他功用执行自定义逻辑。
- 回来response。
具体阐明可看官方文档:fastapi.tiangolo.com/tutorial/mi…
下面是无敌哥pity渠道代码,具体可看GitHub
async def set_body(request: Request, body: bytes):
async def receive() -> Message:
return {"type": "http.request", "body": body}
request._receive = receive
async def get_body(request: Request) -> bytes:
body = await request.body()
await set_body(request, body)
return body
@pity.middleware("http")
async def errors_handling(request: Request, call_next):
body = await request.body()
try:
await set_body(request, await request.body())
return await call_next(request)
except Exception as exc:
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder({
"code": 110,
"msg": str(exc),
"request_data": body,
})
)
参阅issues:github.com/tiangolo/fa…
stackoverflow.com/questions/6…
自定义路由类-APIRoute
在某些情况下,您可能期望掩盖Request和APIRoute类使用的逻辑。特别是,这可能是中间件中逻辑的一个很好的代替方案。例如,如果您想在使用程序处理恳求正文之前读取或操作恳求正文。
具体阐明可看官方文档:fastapi.tiangolo.com/advanced/cu…
class ErrorRouter(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Union[Response]:
try:
return await original_route_handler(request)
except Exception as exc:
log_msg = f"造数渠道捕获到系统过错:恳求途径:{request.url.path}\n"
params = request.query_params
if params:
log_msg += f"途径参数:{params}\n"
boby = await request.body()
if boby:
body = json.dumps(json.loads(boby),ensure_ascii=False)
log_msg +=f"恳求参数:{body}\n"
if isinstance(exc, NormalException):
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"responseCode": Status.SYSTEM_EXCEPTION.get_code(),
"responseMsg": exc.errorMsg
},
)
elif isinstance(exc, RequestValidationError):
message = ""
for error in exc.errors():
message += str(error.get('loc')[-1]) + ":" + str(error.get("msg")) + ","
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder({
"responseCode": Status.PARAM_ILLEGAL.get_code(),
"responseMsg": Status.PARAM_ILLEGAL.get_msg() + message[:-1]
})
)
log_msg +=f"过错信息:{str(exc.args[0])}"
mylog.error(log_msg)
if PlatConfig.SWITCH == 1:
WeGroupChatBot.send_text(log_msg)
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content=jsonable_encoder({
"responseCode": Status.FAIL.get_code(),
"responseMsg": Status.FAIL.get_msg(),
"errorMsg":str(exc.args[0])
},
))
return custom_route_handler
def APIRouter():
router = AppRouter()
router.route_class = ErrorRouter
return router
一致处理之后,再经过类型判别Exception,回来不同的Response~
注意:用了自定义过错路由,就不能再用 @app.exception_handler否则会重复捕获!!!
参阅issues:github.com/tiangolo/fa…
github.com/tiangolo/fa…
总结
今天介绍了fastapi踩坑之路,期望我们以后不要踩这个坑~遇到报错时,网上找不到材料,第一找无敌哥(无敌卷魔、肝帝!),第二看官方文档,第三看官方的issue!总能处理你的问题!勇敢牛牛,不怕困难!
fastapi学习材料:fastapi.tiangolo.com/