I want to send data from app.post()
to app.get()
using RedirectResponse
.
@app.get('/', response_class=HTMLResponse, name='homepage')
async def get_main_data(request: Request,
msg: Optional[str] = None,
result: Optional[str] = None):
if msg:
response = templates.TemplateResponse('home.html', {'request': request, 'msg': msg})
elif result:
response = templates.TemplateResponse('home.html', {'request': request, 'result': result})
else:
response = templates.TemplateResponse('home.html', {'request': request})
return response
@app.post('/', response_model=FormData, name='homepage_post')
async def post_main_data(request: Request,
file: FormData = Depends(FormData.as_form)):
if condition:
......
......
return RedirectResponse(request.url_for('homepage', **{'result': str(trans)}), status_code=status.HTTP_302_FOUND)
return RedirectResponse(request.url_for('homepage', **{'msg': str(err)}), status_code=status.HTTP_302_FOUND)
result
or msg
via RedirectResponse
, url_for()
to app.get()
?path parameter
or query parameter
? How do I achieve this?I am getting the error starlette.routing.NoMatchFound: No route exists for name "homepage" and params "result".
when trying this way.
Update:
I tried the below:
return RedirectResponse(app.url_path_for(name='homepage')
+ '?result=' + str(trans),
status_code=status.HTTP_303_SEE_OTHER)
The above works, but it works by sending the param as query
param, i.e., the URL looks like this localhost:8000/?result=hello
. Is there any way to do the same thing but without showing it in the URL?
In brief, as explained in this answer and this answer, as well as mentioned by @tiangolo here, when performing a RedirectResponse
from a POST
request route to a GET
request route, the response status code has to change to 303 See Other
. For instance (completet working example is given below):
return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
As for the reason for getting starlette.routing.NoMatchFound
error, this is because request.url_for()
receives path
parameters, not query
parameters. Your msg
and result
parameters are query
ones; hence, the error.
A solution would be to use a CustomURLProcessor
, as suggested in this and this answer, allowing you to pass both path
(if need to) and query
parameters to the url_for()
function and obtain the URL. As for hiding the path
and/or query
parameters from the URL, you can use a similar approach to this answer that uses history.pushState()
(or history.replaceState()
) to replace the URL in the browser's address bar.
Working example can be found below (you can use your own TemplateResponse
in the place of HTMLResponse
).
from fastapi import FastAPI, Request, status
from fastapi.responses import RedirectResponse, HTMLResponse
from typing import Optional
import urllib
app = FastAPI()
class CustomURLProcessor:
def __init__(self):
self.path = ""
self.request = None
def url_for(self, request: Request, name: str, **params: str):
self.path = request.url_for(name, **params)
self.request = request
return self
def include_query_params(self, **params: str):
parsed = list(urllib.parse.urlparse(self.path))
parsed[4] = urllib.parse.urlencode(params)
return urllib.parse.urlunparse(parsed)
@app.get('/', response_class=HTMLResponse)
def event_msg(request: Request, msg: Optional[str] = None):
if msg:
html_content = """
<html>
<head>
<script>
window.history.pushState('', '', "/");
</script>
</head>
<body>
<h1>""" + msg + """</h1>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
else:
html_content = """
<html>
<body>
<h1>Create an event</h1>
<form method="POST" action="/">
<input type="submit" value="Create Event">
</form>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
@app.post('/')
def event_create(request: Request):
redirect_url = CustomURLProcessor().url_for(request, 'event_msg').include_query_params(msg="Succesfully created!")
return RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
Regarding adding query params to url_for()
function, another solution would be using Starlette's starlette.datastructures.URL
, which now provides a method to include_query_params
. Example:
from starlette.datastructures import URL
redirect_url = URL(request.url_for('event_msg')).include_query_params(msg="Succesfully created!")
The request.url_for()
function now returns a starlette.datastructures.URL
object. Hence, you add query parameters as follows:
redirect_url = request.url_for('event_msg').include_query_params(msg="Succesfully created!")