簡體   English   中英

如何在使用 fetch() POST 請求登錄后重定向用戶?

[英]How to redirect the user after login using fetch() POST request?

使用以下 JavaScript 代碼,我請求獲取 firebase 令牌,然后使用fetch()向我的 FastAPI 后端發出POST請求,以便登錄用戶。 然后,在后端,如下所示,我檢查令牌是否有效,如果有效,則返回重定向(即RedirectResponse )。 問題是瀏覽器中的重定向不起作用,仍然是之前的頁面。

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));

         
        })

返回登錄頁面的后端端點,其中包含帶按鈕的 HTML 代碼和loginGoogle function:

@router.get("/entrar")
def login(request: Request):
    return templates.TemplateResponse("login.html", {"request": request})

我調用此POST端點,然后重定向到/1 ,這是一條GET路由, status_code303 ,這就是 @tiangolo 在文檔中指定它從POST重定向到GET路由的方式。

@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"}

這是應該將用戶重定向到的GET端點,但事實並非如此:

@app.get("/1")
def get_landing(request: Request):
    return templates.TemplateResponse("landing.html", {"request": request})

Swagger 測試/login端點的屏幕截圖: 在此處輸入圖像描述

我看到的可能導致它不起作用的主要“問題”是您從 Post 請求到 Get 請求的速度太快了。

在網上搜索后,我偶然發現了這個[BUG] RedirectResponse 從 POST 請求路由到 GET 請求路由,如果你會閱讀這個錯誤,你會看到他們指定有時你可能需要 307 而不是你可以閱讀 307此處響應307 臨時重定向

據此,以下內容應該有所幫助:

import starlette.status as status
from fastapi.responses import RedirectResponse

@router.post("/login")
async def login(x_auth_token: str = Header(None))
    # Implementation details ...
    return RedirectResponse('/1', status_code=status.HTTP_302_FOUND)

@app.get("/1")
def get_landing(request: Request):
    return templates.TemplateResponse("landing.html", {"request": request})

據我所知,此處的解決方案是使用status_code=status.HTTP_302_FOUND您可以在此處了解更多信息: What Is a 302 Status Code?

您還可以參考以下鏈接了解更多信息:

  1. fastapi (starlette) RedirectResponse 重定向到 post 而不是 get 方法
  2. 如何在 FastAPI 中執行 Post/Redirect/Get (PRG)?
  3. [問題] 如何發布/重定向/獲取
  4. 重定向響應

根據評論中的@Chris,您還有以下內容:

當使用fetch()向響應RedirectResponse的服務器發出 HTTP 請求時,將自動遵循重定向響應(如此所述),因為默認情況下redirect模式設置為follow 這意味着用戶不會被重定向到新的 URL,而是fetch()將在幕后遵循該重定向。 您可能希望將redirect設置為manual ,而不是允許您獲取重定向Location並手動導航到新頁面,但事實並非如此,如此所述。

但是,您仍然可以在fetch()請求中使用默認的redirect值,即follow (不需要手動指定它,因為它已經默認設置——在下面的示例中,手動定義它只是為了清楚起見) ,然后使用Response.redirected檢查響應是否是您發出的重定向請求的結果。 如果是這樣,您可以使用Response.url ,它將返回“在任何重定向獲得的最終 URL” ,並使用 JavaScript 的window.location.href ,您可以將用戶重定向到目標 URL (即重定向頁面)。

工作示例

應用程序.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='/welcome',status_code=status.HTTP_302_FOUND)
    else:
        return 'Validation failed'
 

@app.get('/welcome')
async def welcome():
    return 'You have been successfully redirected'

模板/索引.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;
                       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="user@mail.com"><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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM