Используя следующий код 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
:
Пожалуйста, избегайте прикрепления изображений кода и вместо этого прикрепите сам код с правильным форматированием (используя ctrl+k), включите импорт и все остальное, что связано
Готово, извините, ребята!
вы пробовали без указания ключевого слова URL? лайк RedirectResponse("/1", status_code=status.HTTP_303_SEE_OTHER)
да, то же самое
Когда вы отправляете и пробуете перенаправление, вы получаете какие-либо журналы или предупреждения? вообще ничего?
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 ?
хорошо, работает ли какой-либо из ваших операторов возврата? или только печать с первого раза?
Отвечает ли это на ваш вопрос? Как перенаправить пользователя обратно на главную страницу с помощью FastAPI после отправки HTML-формы?
Пожалуйста, ознакомьтесь с этим ответом , а также этим ответом и этим ответом.
ни один из этих ответов не решает мою проблему
Посмотри ответ, который я написал :)
собственно проблема в том, что в свагере если делаю редирект, а то в браузере нет, остается на предыдущей странице.
@alvaroquin, что ты имеешь в виду?
@LidorEliyahuShelef Я обновил вопрос с изображением моего чванства, передавая токен авторизации, если я делаю перенаправление на нужный URL-адрес, но когда я делаю это через браузер, ничего не делает, не перенаправляет меня.
@Chris Я просмотрел вопросы, которые вы мне передали, и ни один из них не решает мою ошибку, и да, я тестирую API, используя базовую кнопку html, которая выполняет onclick() для функции, чтобы получить токен Google из Firebase, а затем делает выборка к моему API, который после проверки должен перенаправить. Может быть, это связано со временем загрузки, почему форма Google должна сначала загружаться для входа в систему, а затем выполнять перенаправление?
@alvaroquin метод точно вызывается? Вы проверили, печатается ли ваша печать?
@Chris Я не знаю, какой код вы имеете в виду под релевантным, у меня действительно есть сообщение, и я делаю перенаправление на получение, кода больше не на что смотреть, есть часть внешнего интерфейса, которая отправляет токен но я не думаю, что этот код имеет отношение к этой проблеме.
@ Крис, я обновил вопрос. извините, что не знаю, как расширить проблему, я новичок.
Основная «проблема», которая, как я вижу, может привести к тому, что это не сработает, заключается в том, что вы делаете это быстро, формируя запрос 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?
Вы также можете обратиться к следующим ссылкам для получения дополнительной информации:
Согласно @Chris в комментариях, у вас также есть следующее:
собственно проблема в том, что в свагере если делаю редирект, а то в браузере нет, остается на предыдущей странице.
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>
Вместо того, чтобы возвращать 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>
<form>
во внешнем интерфейсеЕсли использование запроса fetch()
не является требованием для вашего проекта, вместо этого вы можете использовать обычный HTML <form>
и попросить пользователя нажать кнопку отправить, чтобы отправить запрос POST
на сервер. Таким образом, использование RedirectResponse
на стороне сервера (как показано в Варианте 1) приведет к тому, что пользователь на стороне клиента будет автоматически перенаправлен на целевой URL без каких-либо дополнительных действий.
Рабочие примеры можно найти в этом ответе , а также этом ответе и этом ответе.
Пожалуйста, включите соответствующий код и детали в виде текста. Изображения имеют плохую доступность, требуют от читателя переключения вперед и назад, не позволяют копировать и вставлять код или ссылаться на детали в ответе и делают невозможным поиск каких-либо соответствующих деталей.