admin管理员组文章数量:1317910
Using the following JavaScript code, I make a request to obtain the firebase token, and then a POST
request to my FastAPI backend, using the JavaScript fetch()
method, in order to login the user. Then, in the backend, as can be seen below, I check whether or not the token is valid, and if so, return a redirect (i.e., RedirectResponse
) to another webpage. The problem is that the redirect in the browser does not work, and the previous page remains.
function loginGoogle() {
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
//.currentUser.getToken(provider)
.signInWithPopup(provider)
.then((result) => {
/** @type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.idToken;
// The signed-in user info.
var user = result.user;
// ...
})
.catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});
firebase.auth().currentUser.getIdToken(true).then(function(idToken) {
console.log(idToken)
const token = idToken;
const headers = new Headers({
'x-auth-token': token
});
const request = new Request('http://localhost:8000/login', {
method: 'POST',
headers: headers
});
fetch(request)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
})
The endpoint in the backend that returns the login page that contains the HTML code with the button and the loginGoogle
function:
@router.get("/entrar")
def login(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
I call this POST
endpoint and then a redirect to /1
which is a GET
route, and with status_code
being 303
, which is how @tiangolo specifies it in the doc to redirect from a POST
to a GET
route.
@router.post("/login")
async def login(x_auth_token: str = Header(None)):
valid_token = auth.verify_id_token(x_auth_token)
if valid_token:
print("token validado")
return RedirectResponse(url="/1", status_code=status.HTTP_303_SEE_OTHER)
else:
return {"msg": "Token no recibido"}
This is the GET
endpoint to which the user should be redirected, but it doesn't:
@app.get("/1")
def get_landing(request: Request):
return templates.TemplateResponse("landing.html", {"request": request})
Swagger screenshot of testing the /login
endpoint:
Using the following JavaScript code, I make a request to obtain the firebase token, and then a POST
request to my FastAPI backend, using the JavaScript fetch()
method, in order to login the user. Then, in the backend, as can be seen below, I check whether or not the token is valid, and if so, return a redirect (i.e., RedirectResponse
) to another webpage. The problem is that the redirect in the browser does not work, and the previous page remains.
function loginGoogle() {
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
//.currentUser.getToken(provider)
.signInWithPopup(provider)
.then((result) => {
/** @type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.idToken;
// The signed-in user info.
var user = result.user;
// ...
})
.catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});
firebase.auth().currentUser.getIdToken(true).then(function(idToken) {
console.log(idToken)
const token = idToken;
const headers = new Headers({
'x-auth-token': token
});
const request = new Request('http://localhost:8000/login', {
method: 'POST',
headers: headers
});
fetch(request)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
})
The endpoint in the backend that returns the login page that contains the HTML code with the button and the loginGoogle
function:
@router.get("/entrar")
def login(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
I call this POST
endpoint and then a redirect to /1
which is a GET
route, and with status_code
being 303
, which is how @tiangolo specifies it in the doc to redirect from a POST
to a GET
route.
@router.post("/login")
async def login(x_auth_token: str = Header(None)):
valid_token = auth.verify_id_token(x_auth_token)
if valid_token:
print("token validado")
return RedirectResponse(url="/1", status_code=status.HTTP_303_SEE_OTHER)
else:
return {"msg": "Token no recibido"}
This is the GET
endpoint to which the user should be redirected, but it doesn't:
@app.get("/1")
def get_landing(request: Request):
return templates.TemplateResponse("landing.html", {"request": request})
Swagger screenshot of testing the /login
endpoint:
- Please include the relevant code and details as text. Images have bad accessibility, requires the reader to switch back and forth, doesn't allow for copy and pasting the code or referencing details in an answer, and makes it impossible to search for any relevant details. – MatsLindh Commented Jan 20, 2023 at 13:25
- 1 INFO: 127.0.0.1:53670 - "POST /login HTTP/1.1" 303 See Other INFO: 127.0.0.1:53670 - "GET /1 HTTP/1.1" 200 OK can the error be that I am sending a post to a get? – alvaroquin Commented Jan 20, 2023 at 13:56
- Does this answer your question? How to redirect the user back to the home page using FastAPI, after submitting an HTML form? – Chris Commented Jan 20, 2023 at 13:59
- Please have a look at this answer, as well as this answer and this answer. – Chris Commented Jan 20, 2023 at 14:00
- none of these answers solves my problem – alvaroquin Commented Jan 20, 2023 at 14:17
1 Answer
Reset to default 8Option 1 - Returning RedirectResponse
When using the fetch()
function to make an HTTP request to a server that responds with a RedirectResponse
, the redirect response will be automatically followed on client side (as explained here), as the redirect
mode is set to follow
, by default, in the fetch()
function. This means that the user won't be redirected to the new URL, but rather fetch()
will follow that redirection behind the scenes and return the response from the redirect URL. You might expected that setting redirect
to manual
instead would allow you to get the redirect URL (contained in the Location
response header) and manually navigate to the new page, but this is not the case, as described here.
However, you could still use the default redirect
mode in the fetch()
request, i.e., follow
(no need to manually specify it, as it is already set by default—in the example below, it is manually defined for clarity purposes only), and then use the Response.redirected
property, in order to check whether or not the response is the result of a request that you made which was redirected. If so, you can use Response.url
, which will return the "final URL obtained after any redirects", and using JavaScript's window.location.href
, you can redirect the user to the target URL (i.e., the redirect page).
Instead of window.location.href
, one could also use window.location.replace()
. The difference between the two is that when using location.replace()
, after navigating to the given URL, the current page will not be saved in the session history—meaning that the user won't be able to use the back button to navigate to it (which might actually be the way one likes their frontend to behave in such cases).
Working Example
app.py
from fastapi import FastAPI, Request, status, Depends
from fastapi.templating import Jinja2Templates
from fastapi.responses import RedirectResponse
from fastapi.security import OAuth2PasswordRequestForm
app = FastAPI()
templates = Jinja2Templates(directory='templates')
@app.get('/')
async def index(request: Request):
return templates.TemplateResponse('index.html', {'request': request})
@app.post('/login')
async def login(data: OAuth2PasswordRequestForm = Depends()):
# perform some validation, using data.username and data.password
credentials_valid = True
if credentials_valid:
return RedirectResponse(url='/wele',status_code=status.HTTP_302_FOUND)
else:
return 'Validation failed'
@app.get('/wele')
async def wele():
return 'You have been successfully redirected'
templates/index.html
<!DOCTYPE html>
<html>
<head>
<script>
document.addEventListener("DOMContentLoaded", (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
e.preventDefault(); // Cancel the default action
var formElement = document.getElementById('myForm');
var data = new FormData(formElement);
fetch('/login', {
method: 'POST',
redirect: 'follow',
body: data,
})
.then(res => {
if (res.redirected) {
window.location.href = res.url; // or, location.replace(res.url);
return;
}
else
return res.text();
})
.then(data => {
document.getElementById("response").innerHTML = data;
})
.catch(error => {
console.error(error);
});
});
});
</script>
</head>
<body>
<form id="myForm">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username" value="[email protected]"><br>
<label for="password">Password:</label><br>
<input type="password" id="password" name="password" value="pa55w0rd"><br><br>
<input type="submit" value="Submit" class="submit">
</form>
<div id="response"></div>
</body>
</html>
Option 2 - Returning JSON response containing the redirect URL
Instead of returning a RedirectResponse
from the server, you could have the server returning a normal JSON response with the URL included in the JSON object. On client side, you could check whether the JSON object returned from the server—as a result of the fetch()
request—includes the url
key, and if so, retrieve its value and redirect the user to the target URL, using JavaScript's window.location.href
or window.location.replace()
.
Alternatively, one could add the redirect URL to a custom response header on server side (see examples here and here on how to set a response header in FastAPI), and access it on client side, after posting the request using fetch()
, as shown here (Note that if you were doing a cross-origin request, you would have to set the Access-Control-Expose-Headers
response header on server side (see examples here and here, as well as FastAPI's CORSMiddleware
documentation on how to use the expose_headers
argument), indicating that your custom response header, which includes the redirect URL, should be made available to JS scripts running in the browser, since only the CORS-safelisted response headers are exposed by default).
Working Example
app.py
from fastapi import FastAPI, Request, status, Depends
from fastapi.templating import Jinja2Templates
from fastapi.security import OAuth2PasswordRequestForm
app = FastAPI()
templates = Jinja2Templates(directory='templates')
@app.get('/')
async def index(request: Request):
return templates.TemplateResponse('index.html', {'request': request})
@app.post('/login')
async def login(data: OAuth2PasswordRequestForm = Depends()):
# perform some validation, using data.username and data.password
credentials_valid = True
if credentials_valid:
return {'url': '/wele'}
else:
return 'Validation failed'
@app.get('/wele')
async def wele():
return 'You have been successfully redirected'
templates/index.html
<!DOCTYPE html>
<html>
<head>
<script>
document.addEventListener("DOMContentLoaded", (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
e.preventDefault(); // Cancel the default action
var formElement = document.getElementById('myForm');
var data = new FormData(formElement);
fetch('/login', {
method: 'POST',
body: data,
})
.then(res => res.json())
.then(data => {
if (data.url)
window.location.href = data.url; // or, location.replace(data.url);
else
document.getElementById("response").innerHTML = data;
})
.catch(error => {
console.error(error);
});
});
});
</script>
</head>
<body>
<form id="myForm">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username" value="[email protected]"><br>
<label for="password">Password:</label><br>
<input type="password" id="password" name="password" value="pa55w0rd"><br><br>
<input type="submit" value="Submit" class="submit">
</form>
<div id="response"></div>
</body>
</html>
Option 3 - Using HTML <form>
in the frontend
If using a fetch()
request is not a requirement for your project, you could instead use a normal HTML <form>
and have the user click on the submit
button to send the POST
request to the server. In this way, using a RedirectResponse
on server side (as demonstrated in Option 1) would result in having the user on client side automatically be redirected to the target URL, without any further action.
Working examples can be found in this answer, as well as this answer and this answer.
本文标签: pythonHow to redirect the user to another page after login using JavaScript Fetch APIStack Overflow
版权声明:本文标题:python - How to redirect the user to another page after login using JavaScript Fetch API? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742027255a2415777.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论