Code Monkey home page Code Monkey logo

Comments (14)

tiangolo avatar tiangolo commented on April 28, 2024 21

@wozniakty yep, thanks. It's updated now.

@teuneboon if you have declared a path operation with /users it should work as-is. If it's /users/ then Starlette/FastAPI will try to redirect.

If it's the main path operation under a router for /users, you can now declare a path of "" instead of "/" and it will accept the /users even if that's a sub-router.

from fastapi.

euri10 avatar euri10 commented on April 28, 2024 1

Tested and indeed redirect happens using a curl -L to follow redirects:

 curl -L -v http://127.0.0.1:8001/users   
* Expire in 0 ms for 6 (transfer 0x563800ebcd30)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x563800ebcd30)
* Connected to 127.0.0.1 (127.0.0.1) port 8001 (#0)
> GET /users HTTP/1.1
> Host: 127.0.0.1:8001
> User-Agent: curl/7.64.0
> Accept: */*
> 
< HTTP/1.1 302 Found
< server: uvicorn
< date: Mon, 25 Feb 2019 16:05:22 GMT
< location: http://127.0.0.1:8001/users/
< transfer-encoding: chunked
< 
* Ignoring the response-body
* Connection #0 to host 127.0.0.1 left intact
* Issue another request to this URL: 'http://127.0.0.1:8001/users/'
* Found bundle for host 127.0.0.1: 0x563800ebbd60 [can pipeline]
* Could pipeline, but not asked to!
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 8001 (#0)
* Expire in 0 ms for 6 (transfer 0x563800ebcd30)
> GET /users/ HTTP/1.1
> Host: 127.0.0.1:8001
> User-Agent: curl/7.64.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< server: uvicorn
< date: Mon, 25 Feb 2019 16:05:22 GMT
< content-length: 16
< content-type: application/json
< 
* Connection #0 to host 127.0.0.1 left intact
{"users":"root"}%         

server-side log:

backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - Connected
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [8] Initialized {'type': 'http', 'http_version': '1.1', 'server': ('172.28.0.2', 8000), 'client': ('172.28.0.1', 59822), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/users', 'query_string': b'', 'headers': '<...>'}
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [8] Started task
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [8] Received {'type': 'http.response.start', 'status': 302, 'headers': '<...>'}
backend_1_79e424196ea0 | INFO:uvicorn:('172.28.0.1', 59822) - "GET /users HTTP/1.1" 302
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [8] Received {'type': 'http.response.body', 'body': '<0 bytes>'}
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [8] Completed
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [9] Initialized {'type': 'http', 'http_version': '1.1', 'server': ('172.28.0.2', 8000), 'client': ('172.28.0.1', 59822), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/users/', 'query_string': b'', 'headers': '<...>'}
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [9] Started task
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [9] Received {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
backend_1_79e424196ea0 | INFO:uvicorn:('172.28.0.1', 59822) - "GET /users/ HTTP/1.1" 200
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [9] Received {'type': 'http.response.body', 'body': '<16 bytes>'}
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - ASGI [9] Completed
backend_1_79e424196ea0 | DEBUG:uvicorn:('172.28.0.1', 59822) - Disconnected

from fastapi.

tiangolo avatar tiangolo commented on April 28, 2024 1

I forgot to mention, thanks @euri10 for your help here. As always, greatly appreciated! 🌮 🎉 🍰

from fastapi.

wozniakty avatar wozniakty commented on April 28, 2024 1

@tiangolo Although this is closed, just calling out that starlette just fixed an issue with redirects on non-get methods that would be great to get in someday:
encode/starlette@3f70223
It requires upgrading starlette to 0.12.7 but that's incompatible with the pinned version in FastAPI.

from fastapi.

teuneboon avatar teuneboon commented on April 28, 2024 1

I'm not sure if this is the right place to ask this, but it looks like the redirect that happens here breaks our use-case. We host our api at https://url/api, using Apache to make sure /api points to FastAPI, FastAPI doesn't receive that it's behind /api(it just gets a request to /users/ instead of /api/users/). For the docs to still work in this case I add openapi_prefix='/api' to the FastAPI definition. Now, that's all fine and working, but if I request http://url/api/users (no trailing slash) FastAPI wants to try and redirect it to http://url/users/, which is invalid and gives a 404. Is it possible to tell FastAPI/Starlette that we're running behind /api for redirects like this?

from fastapi.

alexiri avatar alexiri commented on April 28, 2024

I had this issue as well, in the end I stopped using prefixes so I could have the /items endpoint. Not ideal...

from fastapi.

euri10 avatar euri10 commented on April 28, 2024

We are forced to have an ending '/' on the collection endpoints, e.g. /items/ vs /items/{id} This is I think the more formal REST practice, but it results in a 404 when someone uses /items instead - I'd rather follow the mantra of 'be flexible with regards to user input and strict with what I return'. I'd like to have the option of allowing both /items and /items/ for the collection endpoints.

Sorry in advance if I'm totally off, maybe I don't get what you mean, but I don't seem to have the issue you describe.

If for instance I have that users router, using prefix as described

app = FastAPI()
app.include_router(users_router, prefix='/users')

then for instance, inside my users router I have those routes for instance:

router = APIRouter()


@router.get("/list", response_model=List[User], tags=["Users"])
async def list():
    query = users.select()
    result = await database.fetch_all(query)
    return [r._row for r in result]


@router.get("/me", response_model=User, tags=["Users"])
async def users_me(current_user: User = Depends(get_current_active_user)):
    return current_user

As you'll see in image attached none of the routes end with a slash, or that's what you'd expect ?

Imgur

from fastapi.

wshayes avatar wshayes commented on April 28, 2024

Typically - one would use the GET /users to list the users and GET /users/{id} to get one user specified by id

Using the prefix option, if you use /users as the prefix and '/' as the path in the sub-route module, this results in /users/ and no way to generate /users.

I guess that prefix wasn't really designed for this purpose and is more for something like /api/v1 as the prefix and then add in the /users in the sub-route user module.

from fastapi.

tiangolo avatar tiangolo commented on April 28, 2024

I think I understand the problem.

First: declaring a path operation /users/, and having a client request /users, should (optionally) redirect to or return the same as /users/.

Then, using include_router, should have the same behavior.


I think there is even code in Starlette (although not documented) to handle it.

I'll check it as soon as I'm in front of my laptop (I'm on a trip for a week).

from fastapi.

tiangolo avatar tiangolo commented on April 28, 2024

Yep. I think @euri10 is right.

It seems to be working correctly now.


More details:

In recent versions of Starlette, that is handled automatically, as you expect it. And I have updated FastAPI recently, to be compatible with/pinned to the latest versions of Starlette.

Please, update your version of FastAPI with, e.g.:

pip install --upgrade fastapi

And let me know if it's working for you @wshayes and @alexiri.


To test it, you can just copy-paste the files for the tutorial: https://fastapi.tiangolo.com/tutorial/bigger-applications/ (or copy them directly form the source: https://github.com/tiangolo/fastapi/tree/master/docs/src/bigger_applications)

And then open your browser at: http://127.0.0.1:8000/users.

It should redirect you to: http://127.0.0.1:8000/users/

And return a JSON like:

[
  {"username":"Foo"},
  {"username":"Bar"}
]

from fastapi.

hjkelly avatar hjkelly commented on April 28, 2024

I'm just tuning in (to the project and this issue), but it seems everyone concluded that Starlette is responsible and seems to have added redirects when you miss a slash (but not when you provide an unnecessary one). But even so, does that solve the case from @wshayes' original screenshot with POST requests? I still lose that data and it's remapped to GET.

If I'm not wrong about that, here are some ideas:

  1. I commented on a Starlette issue suggesting that we not care about trailing slashes at all. Can anyone think of a downside to that?

  2. In the meantime, I figured I'd try adding a /? to all my route decorators, and that seems to work great for all cases! However, then the docs include this trailing slash. Perhaps it'd be sufficient to clean that up before generating docs?

    Screen Shot 2019-05-04 at 10 16 23 PM

Ironically, I had trouble loading the docs at first. I was getting a 404 and assumed I had forgotten some plugin... then I realized I was calling /docs/ and it didn't honor the trailing slash. ;)

Loving the project so far! Let me know how I can help.

from fastapi.

tiangolo avatar tiangolo commented on April 28, 2024

@hjkelly Yep, it's the thing with REST, that it's not very strict (sometimes it's good, sometimes confusing).

About use cases for endpoints without trailing slash, sometimes trailing slashes are used for plurals (items) and non-trailing for things that are single, or that don't return several items (or several X). It might also be useful for declaring actions, like, let's say /publish.

About the specific case of having /docs/ and /redoc/ instead of /docs and /redoc, I would consider it. Would you like to create a new issue adding it as a feature request? I would like to see if anyone opposes to it, I want to check if that would break any use case.

from fastapi.

dmontagu avatar dmontagu commented on April 28, 2024

@teuneboon

You can currently fix this behavior by subclassing fastapi.APIRouter and overriding APIRouter.__call__ with a change to this line:
https://github.com/encode/starlette/blob/master/starlette/routing.py#L610

Then use the subclassed router to decorate your endpoints.

But it shouldn't be so awkward to specify a modified redirect path in this way. I think you should create an issue for this in starlette, I think it would probably be well-received, and based on the fact that that method isn't overridden at all in fastapi, fixing it in starlette would probably be a prerequisite for incorporating the fix into fastapi.

from fastapi.

github-actions avatar github-actions commented on April 28, 2024

Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs.

from fastapi.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.