如何让Uvicorn将非RFC合规URL传递给FastAPI处理?
解决Uvicorn拦截非RFC标准URL的问题
Uvicorn会在请求解析阶段拦截不符合RFC标准的URL(比如带空格、未编码Unicode的地址)并直接返回400错误,要让这类请求流转到FastAPI处理,可通过自定义ASGI中间件捕获Uvicorn的URL解析异常,重新构造请求后传递给FastAPI。
实现步骤
1. 编写异常捕获中间件
这个中间件会捕捉Uvicorn抛出的InvalidURL异常,从原始请求中提取未解析的路径并修改请求scope,让FastAPI能接收并处理该请求:
from uvicorn.errors import InvalidURL from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request class InvalidURLMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): try: return await call_next(request) except InvalidURL: # 从scope中获取原始字节路径,用latin-1解码避免编码错误 raw_path = request.scope.get("raw_path", b"") request.scope["path"] = raw_path.decode("latin-1") # 重新将请求传递给FastAPI return await call_next(request)
2. 配置FastAPI并添加兼容路由
将中间件挂载到FastAPI实例,同时添加一个通配路由处理所有非法URL,按需返回格式化响应或兼容旧API逻辑:
from fastapi import FastAPI, Request from fastapi.responses import JSONResponse app = FastAPI() # 挂载自定义中间件 app.add_middleware(InvalidURLMiddleware) # 通配路由,匹配所有路径(含非法URL) @app.api_route("/{full_path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"]) async def handle_invalid_url(request: Request, full_path: str): # 这里可替换为旧API的兼容逻辑,或返回格式化错误 return JSONResponse( status_code=400, content={ "code": "INVALID_URL", "message": "请求URL格式不符合标准", "original_path": full_path } ) # 原有正常业务路由 @app.get("/api/v1/health") async def health_check(): return {"status": "ok"}
关键说明
- 用
latin-1解码原始路径是因为它能处理所有字节类型,不会因特殊字符抛出解码异常; - 通配路由
/{full_path:path}会匹配所有层级的路径,确保所有非法URL都能被捕获; - 若需完全兼容旧API行为,可在
handle_invalid_url中实现对应的请求处理逻辑,而非直接返回错误。
内容的提问来源于stack exchange,提问作者Xerxys
相关产品推荐
相关产品推荐

