Fastapi: [BUG] RedirectResponse from a POST request route to GET request route shows 405 Error code.

Created on 31 May 2020  路  5Comments  路  Source: tiangolo/fastapi

_Summary of the total issue is:_ How to do a Post/Redirect/Get (PRG) in FastAPI?

_This is not necessarily a bug, rather a question._

Things i tried:

I want to redirect response from 2nd route to 1st route. This Issue#199 here explains GET to GET but not a POST to GET. N.B: I have done this type of POST -> GET redirecting in flask, it was working there but not here. And also this Issue#863 has the same problem but doesn't really solves the problem. To re produce the error check the bottom.

#1st route (GET request)
@admin_content_edit_router.get('/admin/edit_content/set_category')
async def set_category(request:Request):
     return templates.TemplateResponse("admin/category_edit.html", {'request': request})

#2nd route (POST request)
@admin_content_edit_router.post('/admin/edit_content/add_category')
async def add_category(request:Request):
        # here forms are getting processed
        return RedirectResponse(app.url_path_for('set_category')) # from here to 1st route

But it shows :

 {"detail":"Method Not Allowed"}

Full traceback:

INFO:     127.0.0.1:58415 - "POST /admin/edit_content/add_category HTTP/1.1" 307 Temporary Redirect
INFO:     127.0.0.1:58415 - "POST /admin/edit_content/set_category HTTP/1.1" 405 Method Not Allowed
ERROR:    Exception in callback _SelectorSocketTransport._read_ready()
handle: <Handle _SelectorSocketTransport._read_ready()>
Traceback (most recent call last):
  File "c:\users\aminp\appdata\local\programs\python\python36\lib\asyncio\events.py", line 145, in _run
    self._callback(*self._args)
  File "c:\users\aminp\appdata\local\programs\python\python36\lib\asyncio\selector_events.py", line 730, in _read_ready
    self._protocol.data_received(data)
  File "c:\users\aminp\appdata\local\programs\python\python36\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 162, in data_received
    self.handle_events()
  File "c:\users\aminp\appdata\local\programs\python\python36\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 247, in handle_events
    self.transport.resume_reading()
  File "c:\users\aminp\appdata\local\programs\python\python36\lib\asyncio\selector_events.py", line 711, in resume_reading
    raise RuntimeError('Not paused')
RuntimeError: Not paused

But when i do a GET to GET redirect response it works without any issue but a POST to GET blows things up! Am i completely missing something here? i did look up in starlette doc here on reverse route lookup but nothing helps. https://www.starlette.io/routing/#reverse-url-lookups

Quick Re produce the error:


from fastapi import FastAPI
from starlette.responses import RedirectResponse
import os
from starlette.status import HTTP_302_FOUND,HTTP_303_SEE_OTHER

app = FastAPI()

@app.post("/")
async def login():
     # HTTP_302_FOUND,HTTP_303_SEE_OTHER : None is working:(
     return RedirectResponse(url="/ressource/1",status_code=HTTP_303_SEE_OTHER)

@app.get("/ressource/{r_id}")
async def get_ressource(r_id:str):
     return {"r_id": r_id}

if __name__ == '__main__':
    os.system("uvicorn tes:app --host 0.0.0.0 --port 80")
answered bug

All 5 comments

RedirectResponse uses 307 status by default: https://www.starlette.io/responses/#redirectresponse

The method and the body of the original request are reused to perform the redirected request. In the cases where you want the method used to be changed to GET, use 303 See Other instead. This is useful when you want to give an answer to a PUT method that is not the uploaded resources, but a confirmation message (like "You successfully uploaded XYZ").

The only difference between 307 and 302 is that 307 guarantees that the method and the body will not be changed when the redirected request is made. With 302, some old clients were incorrectly changing the method to GET: the behavior with non-GET methods and 302 is then unpredictable on the Web, whereas the behavior with 307 is predictable. For GET requests, their behavior is identical.

https://developer.mozilla.org/docs/Web/HTTP/Status/307

To override the status code, pass parameter status_code=307 to RedirectResponse.

To override the status code, pass parameter status_code=307 to RedirectResponse.

Are you suggesting below snippet like this?:
return RedirectResponse(url="/ressource/1",status_code=HTTP_307_TEMPORARY_REDIRECT)

Well this doesn't really solve the problem, same error:
"GET / HTTP/1.1" 405 Method Not Allowed
Can you please try the Quick Re Produce code snippet and see what works for you, if you please!:)

If no one has already I'd like to give this a go :)

Thanks for the help here @phy25 and for the other @gmelodie ! :rocket:

I actually can't reproduce it... :thinking:

Here's a small app that seems to be working:

from fastapi import FastAPI, status
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.post("/")
async def login():
    return RedirectResponse(url="/ressource/1", status_code=status.HTTP_303_SEE_OTHER)


@app.get("/ressource/{r_id}")
async def get_ressource(r_id: str):
    return {"r_id": r_id}

When using the docs UI, open the browser developer tools, in the "Network" tab, to see the redirection underneath:

Selection_063

You can see that after the POST there's a GET for your path operation.

It's working. Thanks a lot.@tiangolo

Was this page helpful?
0 / 5 - 0 ratings