I have a problem: I can’t understand how I can pass additional data to the redirect
I read the answers: this, this, this but these links did not give me clarity
in HTML file script:
function cmb_pos() {
var data = {
'dep_id': dep_id, 'div_id': div_id,
}
fetch('/job-history/add-new/intermediate/{{persona_id}}', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(response => console.log(JSON.stringify(response)))
};
first: (a function that accepts a POST, performs actions and redirects to another function)
from starlette.datastructures import URL
@router.post(path="/add-new/intermediate/{persona_id}",
response_class=responses.RedirectResponse,
)
async def my_test(request: Request, item: Item, persona_id: int, service: Related):
staff_schedule = await service.get_item_by_where(....)
context_ext = {}
context_ext["request"] = request
context_ext["staff_schedule"] = staff_schedule
context_ext["flag"] = True
redirect_url = URL(request.url_for("create_job_history_init", persona_id=persona_id))
.include_query_params(context_ext=context_ext)
return responses.RedirectResponse(
url=redirect_url,
status_code=status.HTTP_303_SEE_OTHER,
)
second: (function to which the redirect occurs)
@router.get(path="/persona_id={persona_id}/add-new",
response_class=responses.HTMLResponse,
)
async def create_job_history_init(request: Request, persona_id: int,
schedule_service: CommonsDep, context_ext: Optional[dict] = None,
):
context = {}
schedule = await schedule_service.get_all_item()
related = ......
context = schedule | related
context["request"] = request
context["persona_id"] = persona_id
context["context_ext"] = context_ext
return backend.webapps.templates.TemplateResponse(
name="job_history/create_job_history.html",
context=context,
)
I got this error:
File "/Users/pas/------/backend/webapps/job_history.py", line 276, in my_test ).include_query_params(context_ext=context_ext)
File "/Users/pas/-------/.my_pers/lib/python3.11/site-packages/starlette/datastructures.py", line 143, in include_query_params params = MultiDict(parse_qsl(self.query, keep_blank_values=True))
File "/Users/pas/---------/.my_pers/lib/python3.11/site-packages/starlette/datastructures.py", line 83, in query return self.components.query
File "/Users/pas/---------/.my_pers/lib/python3.11/site-packages/starlette/datastructures.py", line 66, in components self._components = urlsplit(self._url)
TypeError: unhashable type: 'URL'
what am I doing wrong? Is it possible to transfer additional data via a redirect?
thank you very much in advance
update: in this This solution is proposed, but I get an error in it
return tuple(x.decode(encoding, errors) if x else '' for x in args)
AttributeError: 'URL' object has no attribute 'decode'
parsed = list(urllib.parse.urlparse(redirect_url))
for me the right solution is this:
parsed = list(urllib.parse.urlparse(redirect_url ._url )) after adding ._url I was able to parse
Current Approach
You are using JavaScript Fetch API to make a
POST
request with JSON data to theceate_job_history_intermediate()
endpoint of your API. Next, that endpoint responds with aRedirectResponse
to aGET
endpoint, after converting the JSON (body) data into query parameters and adding them to the URL (Note: when redirecting from aPOST
endpoint to aGET
endpoint, the response status code has to change to303
or302
or301
. Please take a look at the following posts for more details and examples: here, here, as well as here, here and here). However, as you are using a JavaScriptfetch()
request, the browser can't redirect you to the new webpage, unless you follow one of the approaches described in this answer.Issues with Current Approach
In HTTP, a redirect response with status codes that fall under the
3xx
category, as mentioned above, indicate that a requested resource has been moved to a new location, and the browser should redirect the request to the new location. Hence, when returning aRedirectResponse
from theceate_job_history_intermediate()
endpoint (which, as you may soon realise, is not needed at all), this response actually goes back to the browser first, which is instructed to issue aGET
request with the data (that you submitted earlier) now being part of the query string. As you may understand, this approach makes little sense. You might as well send the data as query parameters in the first place, or, preferably, asForm
data in the request body, as passing sensitive data in the query string poses serious security/privacy risks—have a look at the top of this answer for more details on that subject. The following solutions implement the aforementioned approaches, using an HTML<form>
instead of JavaScript Fetch API. However, in the case of passing the data as part of the query string, you might as well use afetch()
request instead (see this answer and the following links). Please have a look at these answers from which the below approaches are derived: this and this (you might find this useful as well).Solution 1 - Passing
Form
data in the request bodyapp.py (defining
Form
parameters directly in the endpoint)In case you had multiple
Form
parameters and wanted to define them in a class instead of the endpoint, you could use a@dataclass
, as shown below (see this answer for more details):app.py (defining
Form
parameters using a@dataclass
)templates/index.html
templates/result.html
Solution 2 - Passing data as part of the query string
Again, please note the risks that may come with this approach (when using query parameters), as mentioned above and discussed in this answer.
app.py
templates/index.html
templates/result.html