fastapi (starlette) RedirectResponse 重定向到 post 而不是 get 方法
fastapi (starlette) RedirectResponse redirect to post instead get method
我在返回 RedirectResponse 对象后遇到了奇怪的重定向行为
events.py
router = APIRouter()
@router.post('/create', response_model=EventBase)
async def event_create(
request: Request,
user_id: str = Depends(get_current_user),
service: EventsService = Depends(),
form: EventForm = Depends(EventForm.as_form)
):
event = await service.post(
...
)
redirect_url = request.url_for('get_event', **{'pk': event['id']})
return RedirectResponse(redirect_url)
@router.get('/{pk}', response_model=EventSingle)
async def get_event(
request: Request,
pk: int,
service: EventsService = Depends()
):
....some logic....
return templates.TemplateResponse(
'event.html',
context=
{
...
}
)
routers.py
api_router = APIRouter()
...
api_router.include_router(events.router, prefix="/event")
这段代码returns结果
127.0.0.1:37772 - "POST /event/22 HTTP/1.1" 405 Method Not Allowed
好的,我看到出于某种原因调用了 POST 请求而不是 GET 请求。我搜索解释并发现 RedirectResponse 对象默认为代码 307 并调用 POST
我听从建议并添加状态
redirect_url = request.url_for('get_event', **{'pk': event['id']}, status_code=status.HTTP_302_FOUND)
并得到
starlette.routing.NoMatchFound
为了实验,我将 @router.get('/{pk}', response_model=EventSingle)
更改为 @router.post('/{pk}', response_model=EventSingle)
并且重定向成功完成,但是 post 请求不适合我这里。我做错了什么?
UPD
html 形式 运行 event/create 逻辑
base.html
<form action="{{ url_for('event_create')}}" method="POST">
...
</form>
base_view.py
@router.get('/', response_class=HTMLResponse)
async def main_page(request: Request,
activity_service: ActivityService = Depends()):
activity = await activity_service.get()
return templates.TemplateResponse('base.html', context={'request': request,
'activities': activity})
如果您想在 POST 之后重定向到 GET,最佳做法是 to redirect with a 303
status code,因此只需将您的代码更新为:
# ...
return RedirectResponse(redirect_url, status_code=303)
如您所见,redirecting with 307
keeps the HTTP method and body。
完整的工作示例:
from fastapi import FastAPI, APIRouter, Request
from fastapi.responses import RedirectResponse, HTMLResponse
router = APIRouter()
@router.get('/form')
def form():
return HTMLResponse("""
<html>
<form action="/event/create" method="POST">
<button>Send request</button>
</form>
</html>
""")
@router.post('/create')
async def event_create(
request: Request
):
event = {"id": 123}
redirect_url = request.url_for('get_event', **{'pk': event['id']})
return RedirectResponse(redirect_url, status_code=303)
@router.get('/{pk}')
async def get_event(
request: Request,
pk: int,
):
return f'<html>oi pk={pk}</html>'
app = FastAPI(title='Test API')
app.include_router(router, prefix="/event")
到 运行,安装 pip install fastapi uvicorn
和 运行:
uvicorn --reload --host 0.0.0.0 --port 3000 example:app
然后,将浏览器指向:http://localhost:3000/event/form
您向 is returned as you are trying to access the event_create
endpoint via http://127.0.0.1:8000/event/create, for instance. However, since event_create
route handles POST
requests, your request ends up in get_event
endpoint (and returns a value is not a valid integer
error, since you are passing a string instead of integer), as when you type a URL in the address bar of your browser, it performs a GET
request. Thus, you need a form, for example, to submit a POST
request to the event_create
endpoint. Below is an example, similar to the one provided by @Elias Dorneles, which you can run, for example on port 8000, and access the form at http://127.0.0.1:8000/event/ to send a POST request, which will then lead to the RedirectResponse. As @tiangolo posted here, when performing a RedirectResponse from a POST request route to GET request route, the response status code has to change提到status_code=status.HTTP_303_SEE_OTHER
的错误。
from fastapi import APIRouter, FastAPI, Request, status
from fastapi.responses import RedirectResponse, HTMLResponse
router = APIRouter()
# Endpoint can be accessed at http://127.0.0.1:8000/event/
@router.get('/', response_class=HTMLResponse)
async def event_create_form(request: Request):
html_content = """
<html>
<body>
<h1>Create an event</h1>
<form method="POST" action="/event/create">
<input type="submit" value="Create Event">
</form>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
@router.post('/create')
async def event_create(request: Request):
event = {"id": 1}
redirect_url = request.url_for('get_event', **{'pk': event['id']})
return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
@router.get('/{pk}')
async def get_event(request: Request, pk: int):
return {"pk": pk}
app = FastAPI()
app.include_router(router, prefix="/event")
我在返回 RedirectResponse 对象后遇到了奇怪的重定向行为
events.py
router = APIRouter()
@router.post('/create', response_model=EventBase)
async def event_create(
request: Request,
user_id: str = Depends(get_current_user),
service: EventsService = Depends(),
form: EventForm = Depends(EventForm.as_form)
):
event = await service.post(
...
)
redirect_url = request.url_for('get_event', **{'pk': event['id']})
return RedirectResponse(redirect_url)
@router.get('/{pk}', response_model=EventSingle)
async def get_event(
request: Request,
pk: int,
service: EventsService = Depends()
):
....some logic....
return templates.TemplateResponse(
'event.html',
context=
{
...
}
)
routers.py
api_router = APIRouter()
...
api_router.include_router(events.router, prefix="/event")
这段代码returns结果
127.0.0.1:37772 - "POST /event/22 HTTP/1.1" 405 Method Not Allowed
好的,我看到出于某种原因调用了 POST 请求而不是 GET 请求。我搜索解释并发现 RedirectResponse 对象默认为代码 307 并调用 POST
我听从建议并添加状态
redirect_url = request.url_for('get_event', **{'pk': event['id']}, status_code=status.HTTP_302_FOUND)
并得到
starlette.routing.NoMatchFound
为了实验,我将 @router.get('/{pk}', response_model=EventSingle)
更改为 @router.post('/{pk}', response_model=EventSingle)
并且重定向成功完成,但是 post 请求不适合我这里。我做错了什么?
UPD
html 形式 运行 event/create 逻辑
base.html
<form action="{{ url_for('event_create')}}" method="POST">
...
</form>
base_view.py
@router.get('/', response_class=HTMLResponse)
async def main_page(request: Request,
activity_service: ActivityService = Depends()):
activity = await activity_service.get()
return templates.TemplateResponse('base.html', context={'request': request,
'activities': activity})
如果您想在 POST 之后重定向到 GET,最佳做法是 to redirect with a 303
status code,因此只需将您的代码更新为:
# ...
return RedirectResponse(redirect_url, status_code=303)
如您所见,redirecting with 307
keeps the HTTP method and body。
完整的工作示例:
from fastapi import FastAPI, APIRouter, Request
from fastapi.responses import RedirectResponse, HTMLResponse
router = APIRouter()
@router.get('/form')
def form():
return HTMLResponse("""
<html>
<form action="/event/create" method="POST">
<button>Send request</button>
</form>
</html>
""")
@router.post('/create')
async def event_create(
request: Request
):
event = {"id": 123}
redirect_url = request.url_for('get_event', **{'pk': event['id']})
return RedirectResponse(redirect_url, status_code=303)
@router.get('/{pk}')
async def get_event(
request: Request,
pk: int,
):
return f'<html>oi pk={pk}</html>'
app = FastAPI(title='Test API')
app.include_router(router, prefix="/event")
到 运行,安装 pip install fastapi uvicorn
和 运行:
uvicorn --reload --host 0.0.0.0 --port 3000 example:app
然后,将浏览器指向:http://localhost:3000/event/form
您向event_create
endpoint via http://127.0.0.1:8000/event/create, for instance. However, since event_create
route handles POST
requests, your request ends up in get_event
endpoint (and returns a value is not a valid integer
error, since you are passing a string instead of integer), as when you type a URL in the address bar of your browser, it performs a GET
request. Thus, you need a form, for example, to submit a POST
request to the event_create
endpoint. Below is an example, similar to the one provided by @Elias Dorneles, which you can run, for example on port 8000, and access the form at http://127.0.0.1:8000/event/ to send a POST request, which will then lead to the RedirectResponse. As @tiangolo posted here, when performing a RedirectResponse from a POST request route to GET request route, the response status code has to change提到status_code=status.HTTP_303_SEE_OTHER
的错误。
from fastapi import APIRouter, FastAPI, Request, status
from fastapi.responses import RedirectResponse, HTMLResponse
router = APIRouter()
# Endpoint can be accessed at http://127.0.0.1:8000/event/
@router.get('/', response_class=HTMLResponse)
async def event_create_form(request: Request):
html_content = """
<html>
<body>
<h1>Create an event</h1>
<form method="POST" action="/event/create">
<input type="submit" value="Create Event">
</form>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
@router.post('/create')
async def event_create(request: Request):
event = {"id": 1}
redirect_url = request.url_for('get_event', **{'pk': event['id']})
return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
@router.get('/{pk}')
async def get_event(request: Request, pk: int):
return {"pk": pk}
app = FastAPI()
app.include_router(router, prefix="/event")