握手完成后,protocol changes from HTTP to WebSocket.如果您尝试在 websocket 端点内引发 HTTP 异常,您会看到这是不可能的,或者返回 HTTP 响应(例如,return JSONResponse(...status_code=404)),您将收到内部服务器错误,即 @ 987654332@。
选项1
因此,如果您想在协议升级之前有某种检查机制,则需要使用Middleware,如下所示。在中间件内部,不能引发异常,但可以返回响应(即Response、JSONResponse、PlainTextResponse 等),这实际上是 FastAPI handles exceptions 在幕后的方式。作为参考,请查看此post,以及讨论here。
async def is_user_allowed(request: Request):
# if conditions are not met, return False
print(request['headers'])
print(request.client)
return False
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
if not await is_user_allowed(request):
return JSONResponse(content={"message": "User not allowed"}, status_code=404)
response = await call_next(request)
return response
或者,如果您愿意,您可以使用 is_user_allowed() 方法引发您需要使用 try-except 块捕获的自定义异常:
class UserException(Exception):
def __init__(self, message):
self.message = message
super().__init__(message)
async def is_user_allowed(request: Request):
# if conditions are not met, raise UserException
raise UserException(message="User not allowed.")
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
try:
await is_user_allowed(request)
except UserException as e:
return JSONResponse(content={"message": f'{e.message}'}, status_code=404)
response = await call_next(request)
return response
选项 2
但是,如果您需要使用 websocket 实例来执行此操作,则可以使用与上述相同的逻辑,但是,改为在 is_user_allowed() 方法中传递 websocket 实例,并在 websocket 端点内捕获异常(灵感来自this)。
@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
await ws.accept()
try:
await is_user_allowed(ws)
await handle_conn(ws)
except UserException as e:
await ws.send_text(e.message) # optionally send a message to the client before closing the connection
await ws.close()
但是,在上面,您必须先接受连接,以便在引发异常时调用close() 方法来终止连接。如果你愿意,你可以使用类似下面的东西。但是,如前所述,return 语句将 except 块插入将引发内部服务器错误(即ASGI callable returned without sending handshake.)。
@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
try:
await is_user_allowed(ws)
except UserException as e:
return
await ws.accept()
await handle_conn(ws)