Как перенаправить пользователя на другую страницу после входа в систему с помощью POST-запроса fetch()?

Используя следующий код JavaScript, я делаю запрос на получение токена firebase, а затем POST запрос, используя fetch(), к моему серверу FastAPI, чтобы войти в систему пользователя. Затем в бэкенде, как видно ниже, я проверяю, действителен ли токен, и если да, то возвращаю перенаправление (т. е. 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.info(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.info(data))
            .catch(error => console.error(error));

         
        })

Конечная точка в бэкенде, которая возвращает страницу входа, содержащую HTML-код с кнопкой и функцией loginGoogle:

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

Я называю это POST конечной точкой, а затем перенаправляю на /1, который является GET маршрутом, а status_code является 303, как @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:

Пожалуйста, включите соответствующий код и детали в виде текста. Изображения имеют плохую доступность, требуют от читателя переключения вперед и назад, не позволяют копировать и вставлять код или ссылаться на детали в ответе и делают невозможным поиск каких-либо соответствующих деталей.

MatsLindh 20.01.2023 14:25

Пожалуйста, избегайте прикрепления изображений кода и вместо этого прикрепите сам код с правильным форматированием (используя ctrl+k), включите импорт и все остальное, что связано

Lidor Eliyahu Shelef 20.01.2023 14:25

Готово, извините, ребята!

alvaroquin 20.01.2023 14:41

вы пробовали без указания ключевого слова URL? лайк RedirectResponse("/1", status_code=status.HTTP_303_SEE_OTHER)

Lidor Eliyahu Shelef 20.01.2023 14:48

да, то же самое

alvaroquin 20.01.2023 14:52

Когда вы отправляете и пробуете перенаправление, вы получаете какие-либо журналы или предупреждения? вообще ничего?

Lidor Eliyahu Shelef 20.01.2023 14:54

INFO: 127.0.0.1:53670 - "POST /login HTTP/1.1" 303 См. Другие INFO: 127.0.0.1:53670 - "GET /1 HTTP/1.1" 200 OK может ли быть ошибка в том, что я отправляю сообщение в get ?

alvaroquin 20.01.2023 14:56

хорошо, работает ли какой-либо из ваших операторов возврата? или только печать с первого раза?

Lidor Eliyahu Shelef 20.01.2023 14:58

Пожалуйста, ознакомьтесь с этим ответом , а также этим ответом и этим ответом.

Chris 20.01.2023 15:00

ни один из этих ответов не решает мою проблему

alvaroquin 20.01.2023 15:17

Посмотри ответ, который я написал :)

Lidor Eliyahu Shelef 20.01.2023 15:21

собственно проблема в том, что в свагере если делаю редирект, а то в браузере нет, остается на предыдущей странице.

alvaroquin 20.01.2023 16:24

@alvaroquin, что ты имеешь в виду?

Lidor Eliyahu Shelef 20.01.2023 16:30

@LidorEliyahuShelef Я обновил вопрос с изображением моего чванства, передавая токен авторизации, если я делаю перенаправление на нужный URL-адрес, но когда я делаю это через браузер, ничего не делает, не перенаправляет меня.

alvaroquin 20.01.2023 17:20

@Chris Я просмотрел вопросы, которые вы мне передали, и ни один из них не решает мою ошибку, и да, я тестирую API, используя базовую кнопку html, которая выполняет onclick() для функции, чтобы получить токен Google из Firebase, а затем делает выборка к моему API, который после проверки должен перенаправить. Может быть, это связано со временем загрузки, почему форма Google должна сначала загружаться для входа в систему, а затем выполнять перенаправление?

alvaroquin 20.01.2023 17:49

@alvaroquin метод точно вызывается? Вы проверили, печатается ли ваша печать?

Lidor Eliyahu Shelef 20.01.2023 17:57

@Chris Я не знаю, какой код вы имеете в виду под релевантным, у меня действительно есть сообщение, и я делаю перенаправление на получение, кода больше не на что смотреть, есть часть внешнего интерфейса, которая отправляет токен но я не думаю, что этот код имеет отношение к этой проблеме.

alvaroquin 20.01.2023 18:11

@ Крис, я обновил вопрос. извините, что не знаю, как расширить проблему, я новичок.

alvaroquin 20.01.2023 18:21
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
19
195
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Основная «проблема», которая, как я вижу, может привести к тому, что это не сработает, заключается в том, что вы делаете это быстро, формируя запрос Post для запроса Get.

После некоторого поиска в Интернете я наткнулся на это [ОШИБКА] RedirectResponse с маршрута запроса POST на маршрут запроса GET если вы прочитаете эту ошибку, вы увидите, что они указывают, что иногда вам может понадобиться 307, вместо этого вы можете прочитать про ответ 307 здесь 307 Temporary Redirect.

В соответствии с этим должно помочь следующее:

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, вы можете узнать больше об этом здесь: Что такое код состояния 302?

Вы также можете обратиться к следующим ссылкам для получения дополнительной информации:

  1. fastapi (starlette) RedirectResponse перенаправляет на сообщение вместо получения метода
  2. Как сделать Post/Redirect/Get (PRG) в FastAPI?
  3. [ВОПРОС] Как разместить/перенаправить/получить
  4. RedirectResponse

Согласно @Chris в комментариях, у вас также есть следующее:

собственно проблема в том, что в свагере если делаю редирект, а то в браузере нет, остается на предыдущей странице.

alvaroquin 20.01.2023 16:23
Ответ принят как подходящий

Вариант 1 - Возвращение RedirectResponse

При использовании fetch() для отправки HTTP-запроса к серверу, который отвечает RedirectResponse, ответ перенаправления будет автоматически следовать на стороне клиента (как описано здесь ), так как установлен режим redirect на follow по умолчанию в методе fetch(). Это означает, что пользователь не будет перенаправлен на новый URL-адрес, а скорее fetch() будет следовать этому перенаправлению за кулисами и возвращать ответ с URL-адреса перенаправления. Вы могли бы ожидать, что вместо этого установка redirect на manual позволит вам получить URL-адрес перенаправления (содержащийся в заголовке ответа Location ) и вручную перейти на новую страницу, но это не тот случай, как описано здесь.

Тем не менее, вы все равно можете использовать значение redirect по умолчанию в запросе fetch(), то есть follow (нет необходимости указывать его вручную, так как оно уже установлено по умолчанию — в приведенном ниже примере оно определено вручную только для ясности), и затем используйте Response.redirected , чтобы проверить, является ли ответ результатом перенаправленного вами запроса. Если это так, вы можете использовать Response.url , который вернет «конечный URL-адрес, полученный после любых перенаправлений», и с помощью window.location.href JavaScript вы можете перенаправить пользователя на целевой URL (т.е. страницу перенаправления).

Вместо window.location.href также можно использовать window.location.replace() . Отличие от установки значения свойства href заключается в том, что при использовании метода location.replace() после перехода по указанному URL-адресу текущая страница не будет сохранена в истории сеанса, то есть пользователь не сможет использовать кнопку «Назад». чтобы перейти к нему.

Рабочий пример

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

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

шаблоны/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>

Вариант 2. Возврат ответа JSON, содержащего URL-адрес перенаправления.

Вместо того, чтобы возвращать RedirectResponse с сервера, вы можете заставить сервер возвращать обычный ответ JSON с URL-адресом, включенным в объект JSON. На стороне клиента вы можете проверить, содержит ли объект JSON, возвращенный с сервера в результате запроса fetch(), ключ url, и если да, то получить его значение и перенаправить пользователя на целевой URL-адрес, используя window.location.href или window.location.replace() JavaScript. .

В качестве альтернативы можно добавить URL-адрес перенаправления в собственный заголовок ответа на стороне сервера (см. примеры здесь и здесь о том, как установить заголовок ответа в FastAPI), и получить к нему доступ на стороне клиента, после отправив запрос с помощью fetch(), как показано здесь (Обратите внимание, что если вы делаете запрос между источниками , вам нужно будет установить заголовок ответа Access-Control-Expose-Headers на стороне сервера ( см. примеры здесь и здесь , а также документацию FastAPI CORSMiddleware о том, как использовать аргумент expose_headers), указывая, что ваш собственный заголовок ответа, который включает URL-адрес перенаправления, должен быть доступен для сценариев JS. работает в браузере, так как по умолчанию отображаются только заголовки ответов CORS-безопасного списка).

Рабочий пример

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': '/welcome'}
    else:
        return 'Validation failed'
 

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

шаблоны/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>

Вариант 3 — Использование HTML <form> во внешнем интерфейсе

Если использование запроса fetch() не является требованием для вашего проекта, вместо этого вы можете использовать обычный HTML <form> и попросить пользователя нажать кнопку отправить, чтобы отправить запрос POST на сервер. Таким образом, использование RedirectResponse на стороне сервера (как показано в Варианте 1) приведет к тому, что пользователь на стороне клиента будет автоматически перенаправлен на целевой URL без каких-либо дополнительных действий.

Рабочие примеры можно найти в этом ответе , а также этом ответе и этом ответе.

Другие вопросы по теме