fastapi icon indicating copy to clipboard operation
fastapi copied to clipboard

Add test for self-hosted docs behind a proxy

Open bertomaniac opened this issue 1 year ago • 1 comments

Related to #10978, #11033, #11160 and #11622

No code changes here, but adds a test for static assets when using root_path (behind a proxy)

Note, this test passes in <=0.108.0 and fails beginning at 0.109.0.

bertomaniac avatar Mar 06 '24 16:03 bertomaniac

    response = client.get("/openapi.json")

    response = client.get("/static/swagger.js")

    response = client.get("/static/swagger.css")

    assert "/static/swagger.js" in swagger_html
    assert f"{root_path}/static/swagger.js" not in swagger_html
    assert "/static/swagger.css" in swagger_html
    assert f"{root_path}/static/swagger.css" not in swagger_html

The lines above are actually wrong. Since our app is behind the proxy with prefix '/api', all assets' paths should start with root_path ('/api')

There is an issue (IMO) in Starlette that makes static files inaccessible via path without prefix (those that don't start with root_path). Read more: https://github.com/encode/starlette/discussions/2931

Although, there is still a minor issue here. The code of custom_swagger_ui_html should actually include adding root_path prefix to all assets. So, I think the related code example in docs should be updated.

async def custom_swagger_ui_html(request: Request):
    """
    Sets up a localized version of the Swagger (OpenAPI) docs that can be run without assets from the Internet.
    """

    root_path = request.scope.get("root_path", "").rstrip("/")
    return get_swagger_ui_html(
        openapi_url=root_path + "/openapi.json",
        title=app.title,
        swagger_js_url=root_path + "/static/swagger.js",
        swagger_css_url=root_path + "/static/swagger.css",
    )

In tests, all paths should be updated to start with root_path prefix:

    response = client.get(f"{root_path}/openapi.json")
    assert response.status_code == 200

    response = client.get(f"{root_path}/static/swagger.js")
    assert response.status_code == 200

    response = client.get(f"{root_path}/static/swagger.css")
    assert response.status_code == 200

    assert f"{root_path}/static/swagger.js" in swagger_html
    assert f"{root_path}/static/swagger.css" in swagger_html
    

@bertomaniac, would you like to work on this?

YuriiMotov avatar Jun 10 '25 08:06 YuriiMotov

@YuriiMotov Sure. I could probably look at starting on this in the next couple of weeks.

Just to be clear: You're talking about updating these tests and then updating the documentation as well?

bertomaniac avatar Jun 18 '25 18:06 bertomaniac

IMO, ideally we just need to update docs example (add root_path prefixes to URLs of assets as it's done in the original endpoint function) and parameterize test for that code example from docs to use app with and without root_path (using client = TestClient(app=app, root_path=..)?).

Hope I was clear enough 🙂

Actually, I would close this PR and start new one from scratch to keep it clean and easy to review.

But don't trust me blindly, double-check please

YuriiMotov avatar Jun 18 '25 19:06 YuriiMotov

@YuriiMotov

I think I got things in order. New PR is here: https://github.com/fastapi/fastapi/pull/13825

bertomaniac avatar Jun 24 '25 03:06 bertomaniac

Closing this in favor of new #13825

YuriiMotov avatar Jul 06 '25 19:07 YuriiMotov