spec-first / connexion

Connexion is a modern Python web framework that makes spec-first and api-first development easy.
https://connexion.readthedocs.io/en/latest/
Apache License 2.0
4.49k stars 765 forks source link

Treats trailing slashes differently for swagger and normal endpoints #1884

Open chbndrhnns opened 9 months ago

chbndrhnns commented 9 months ago

Description

Expected behaviour

Consistent (and configurable) behaviour

Actual behaviour

Inconsistent

Steps to reproduce

import httpx
import pytest
from connexion import AsyncApp

spec = {
    "openapi": "3.0.0",
    "info": {
        "title": "test",
        "version": "v1"
    },
    "paths": {
        "/hello": {
            "get": {
                "operationId": "test_.hello",
                "responses": {
                    "200": {
                        "description": "ok",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

async def hello():
    return {}

app = AsyncApp(__name__)
app.add_api(spec)

@pytest.fixture(autouse=True)
def anyio_backend():
    return "asyncio"

@pytest.mark.parametrize(
    "url",
    [
        "/hello",
        "/hello/",
        "/ui",
        "/ui/",
    ],
)
async def test_run(url):
    async with httpx.AsyncClient(app=app, base_url="http://localhost") as client:
        res = await client.get(url)

    assert res.status_code == 200

Additional info:

Output of the commands:

RobbeSneyders commented 8 months ago

Thanks @chbndrhnns,

Not sure if this is inconsistent.

If you change your example spec to (note the trailing slash after hello):

    "paths": {
        "/hello/": {
            "get": {

You get the same behaviour as the UI.

I guess we could remove these two lines though so we use the raw path for the UI, which is configurable by the user: https://github.com/spec-first/connexion/blob/211bdb03f6b20f7dc90fe0037e57cd142c48deb5/connexion/middleware/swagger_ui.py#L130 https://github.com/spec-first/connexion/blob/211bdb03f6b20f7dc90fe0037e57cd142c48deb5/connexion/middleware/swagger_ui.py#L151

chbndrhnns commented 8 months ago

Thanks, you are right about the solution.

there is a discussion about this on starlette side. I would want this to be configurable on. Objection side, as well. However, using a middleware to make it work might also be a workaround.