diff --git a/fastapi/applications.py b/fastapi/applications.py index 5a7170dd6..480d03ab8 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -814,6 +814,13 @@ class FastAPI(Starlette): bool, Doc( """ + To ignore (or not) trailing slashes at the end of URIs. + + For example, by setting `ignore_trailing_slash` to True, + requests to `/auth` and `/auth/` will have the same behaviour. + + By default (`ignore_trailing_slash` is False), the two requests are treated differently. + One of them will result in a 307-redirect. """ ), ] = False, @@ -973,7 +980,8 @@ class FastAPI(Starlette): if ignore_trailing_slash: def ignore_trailing_whitespace_middleware(app): async def ignore_trailing_whitespace_wrapper(scope, receive, send): - scope["path"] = scope["path"].rstrip("/") + if scope["type"] in {"http", "websocket"}: + scope["path"] = scope["path"].rstrip("/") await app(scope, receive, send) return ignore_trailing_whitespace_wrapper self.add_middleware(ignore_trailing_whitespace_middleware) diff --git a/fastapi/routing.py b/fastapi/routing.py index d59701ef5..2b68cb0d1 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -815,6 +815,13 @@ class APIRouter(routing.Router): bool, Doc( """ + To ignore (or not) trailing slashes at the end of URIs. + + For example, by setting `ignore_trailing_slash` to True, + requests to `/auth` and `/auth/` will have the same behaviour. + + By default (`ignore_trailing_slash` is False), the two requests are treated differently. + One of them will result in a 307-redirect. """ ), ] = False, diff --git a/tests/test_ignore_trailing_slash.py b/tests/test_ignore_trailing_slash.py index 16537d328..8cd3c802a 100644 --- a/tests/test_ignore_trailing_slash.py +++ b/tests/test_ignore_trailing_slash.py @@ -8,38 +8,52 @@ router = APIRouter() async def example_endpoint(): return {"msg": "Example"} +@app.get("/example2/") +async def example_endpoint(): + return {"msg": "Example 2"} + @app.websocket("/websocket") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() await websocket.send_text("Websocket") await websocket.close() +@app.websocket("/websocket2/") +async def websocket_endpoint(websocket: WebSocket): + await websocket.accept() + await websocket.send_text("Websocket 2") + await websocket.close() + @router.get("/example") def route_endpoint(): return {"msg": "Routing Example"} +@router.get("/example2/") +def route_endpoint(): + return {"msg": "Routing Example 2"} + app.include_router(router, prefix="/router") client = TestClient(app) def test_ignoring_trailing_slash(): - response = client.get("/example", follow_redirects=False) - assert response.status_code == 200 - assert response.json()["msg"] == "Example" response = client.get("/example/", follow_redirects=False) assert response.status_code == 200 assert response.json()["msg"] == "Example" + response = client.get("/example2", follow_redirects=False) + assert response.status_code == 200 + assert response.json()["msg"] == "Example 2" def test_ignoring_trailing_shlash_ws(): - with client.websocket_connect("/websocket") as websocket: - assert websocket.receive_text() == "Websocket" with client.websocket_connect("/websocket/") as websocket: assert websocket.receive_text() == "Websocket" + with client.websocket_connect("/websocket2") as websocket: + assert websocket.receive_text() == "Websocket 2" def test_ignoring_trailing_routing(): - response = client.get("router/example", follow_redirects=False) - assert response.status_code == 200 - assert response.json()["msg"] == "Routing Example" response = client.get("router/example/", follow_redirects=False) assert response.status_code == 200 assert response.json()["msg"] == "Routing Example" + response = client.get("router/example2", follow_redirects=False) + assert response.status_code == 200 + assert response.json()["msg"] == "Routing Example 2" \ No newline at end of file