mirror of https://github.com/tiangolo/fastapi.git
Support sub-apps
This commit is contained in:
parent
845d0af597
commit
16d138113e
|
|
@ -360,8 +360,11 @@ def get_request_handler(
|
|||
if dependant.call
|
||||
else EndpointContext()
|
||||
)
|
||||
|
||||
if dependant.path:
|
||||
endpoint_ctx["path"] = f"{request.method} {dependant.path}"
|
||||
# For mounted sub-apps, include the mount path prefix
|
||||
mount_path = request.scope.get("root_path", "").rstrip("/")
|
||||
endpoint_ctx["path"] = f"{request.method} {mount_path}{dependant.path}"
|
||||
|
||||
# Read body and auto-close files
|
||||
try:
|
||||
|
|
@ -491,8 +494,9 @@ def get_websocket_app(
|
|||
else EndpointContext()
|
||||
)
|
||||
if dependant.path:
|
||||
endpoint_ctx["path"] = f"WS {dependant.path}"
|
||||
|
||||
# For mounted sub-apps, include the mount path prefix
|
||||
mount_path = websocket.scope.get("root_path", "").rstrip("/")
|
||||
endpoint_ctx["path"] = f"WS {mount_path}{dependant.path}"
|
||||
async_exit_stack = websocket.scope.get("fastapi_inner_astack")
|
||||
assert isinstance(async_exit_stack, AsyncExitStack), (
|
||||
"fastapi_inner_astack not found in request scope"
|
||||
|
|
|
|||
|
|
@ -23,16 +23,28 @@ class ExceptionCapture:
|
|||
|
||||
|
||||
app = FastAPI()
|
||||
sub_app = FastAPI()
|
||||
captured_exception = ExceptionCapture()
|
||||
|
||||
app.mount(path="/sub", app=sub_app)
|
||||
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
@sub_app.exception_handler(RequestValidationError)
|
||||
async def request_validation_handler(request: Request, exc: RequestValidationError):
|
||||
captured_exception.capture(exc)
|
||||
raise exc
|
||||
|
||||
|
||||
@app.exception_handler(ResponseValidationError)
|
||||
@sub_app.exception_handler(ResponseValidationError)
|
||||
async def response_validation_handler(_: Request, exc: ResponseValidationError):
|
||||
captured_exception.capture(exc)
|
||||
raise exc
|
||||
|
||||
|
||||
@app.exception_handler(WebSocketRequestValidationError)
|
||||
@sub_app.exception_handler(WebSocketRequestValidationError)
|
||||
async def websocket_validation_handler(
|
||||
websocket: WebSocket, exc: WebSocketRequestValidationError
|
||||
):
|
||||
|
|
@ -50,6 +62,11 @@ def get_item():
|
|||
return {"name": "Widget"}
|
||||
|
||||
|
||||
@sub_app.get("/items/", response_model=Item)
|
||||
def get_sub_item():
|
||||
return {"name": "Widget"} # pragma: no cover
|
||||
|
||||
|
||||
@app.websocket("/ws/{item_id}")
|
||||
async def websocket_endpoint(websocket: WebSocket, item_id: int):
|
||||
await websocket.accept() # pragma: no cover
|
||||
|
|
@ -57,6 +74,13 @@ async def websocket_endpoint(websocket: WebSocket, item_id: int):
|
|||
await websocket.close() # pragma: no cover
|
||||
|
||||
|
||||
@sub_app.websocket("/ws/{item_id}")
|
||||
async def subapp_websocket_endpoint(websocket: WebSocket, item_id: int):
|
||||
await websocket.accept() # pragma: no cover
|
||||
await websocket.send_text(f"Item: {item_id}") # pragma: no cover
|
||||
await websocket.close() # pragma: no cover
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
|
|
@ -95,6 +119,33 @@ def test_websocket_validation_error_includes_endpoint_context():
|
|||
assert "/ws/" in error_str
|
||||
|
||||
|
||||
def test_subapp_request_validation_error_includes_endpoint_context():
|
||||
captured_exception.exception = None
|
||||
try:
|
||||
client.get("/sub/items/")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
assert captured_exception.exception is not None
|
||||
error_str = str(captured_exception.exception)
|
||||
assert "get_sub_item" in error_str
|
||||
assert "/sub/items/" in error_str
|
||||
|
||||
|
||||
def test_subapp_websocket_validation_error_includes_endpoint_context():
|
||||
captured_exception.exception = None
|
||||
try:
|
||||
with client.websocket_connect("/sub/ws/invalid"):
|
||||
pass # pragma: no cover
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
assert captured_exception.exception is not None
|
||||
error_str = str(captured_exception.exception)
|
||||
assert "subapp_websocket_endpoint" in error_str
|
||||
assert "/sub/ws/" in error_str
|
||||
|
||||
|
||||
def test_validation_error_with_only_path():
|
||||
errors = [{"type": "missing", "loc": ("body", "name"), "msg": "Field required"}]
|
||||
exc = RequestValidationError(errors, endpoint_ctx={"path": "GET /api/test"})
|
||||
|
|
|
|||
Loading…
Reference in New Issue