Сеанс Flask не сохраняется (Postman работает, Javascript-нет)
Я разрабатываю сервер Flask для обмена данными между некоторыми внутренними функциями Python и JavaScript-клиентами через интернет. Я пытаюсь использовать переменную Flask session для хранения пользовательских данных в течение всего времени их взаимодействия с приложением. Я удалил большую часть кода конкретного приложения ниже, но основная проблема, с которой я сталкиваюсь, остается.
Вот мой код для моего (упрощенного) приложения Flask:
import json
import os
from flask import Flask, jsonify, request, session
app = Flask(__name__)
app.secret_key = 'my_secret_key'
@app.route('/', methods=['GET'])
def run():
session['hello'] = 'world'
return jsonify(session['hello'])
@app.route('/update', methods=['POST'])
def update():
return jsonify(session['hello'])
if __name__ == '__main__':
app.run(host='0.0.0.0')
Используя почтальона, я могу сделать запрос GET к мой сервер и получить ожидаемый результат "world". Затем я могу сделать запрос POST с произвольным телом и получить тот же ожидаемый результат "world" (снова используя Postman).
При использовании Chrome я могу посетить IP-адрес своего сервера и увидеть ожидаемый результат "world" на странице. Я также могу вручную сделать запрос GET с помощью Javascript (в консоли Chrome) и получить тот же ответ, что и ожидалось. Однако моя проблема возникает при попытке отправить POST-запрос на сервер с помощью Javascript; сервер показывает KeyError: 'hello' при попытке сделать этот запрос.
Вот Javascript, который я использую для запроса POST:
var url = 'http://my_server_ip/update';
fetch(url, {
method: 'POST',
body: JSON.stringify('arbitrary_string'),
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then(response => response.json())
.then((data) => {
console.log(data);
})
Что здесь не так? Почему я могу сделать запросы GET/POST с Postman просто отлично, но столкнуться с ошибками, делая те же запросы с Javascript?
2 ответов:
После нескольких часов тестирования мне удалось выяснить эту проблему. Хотя я думаю, что ответ @user8212173 подчеркивает проблему, я собираюсь ответить на свой собственный вопрос, потому что то, что я нашел, в конечном счете является более простым решением.
Для того, чтобы запрос POST вернул ожидаемое значение, мне просто нужно было добавить строку
credentials: 'same-origin'в телоfetch. Это выглядит следующим образом:var url = 'http://my_server_ip/update'; fetch(url, { method: 'POST', body: JSON.stringify('arbitrary_string'), credentials: 'same-origin', // this line has been added headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then((data) => { console.log(data); })Согласно руководству по использованию Fetch от Mozilla ,
По умолчанию, fetch не будет отправить или получить файлы cookie с сервера, что приводит к несанкционированным запросам, если сайт полагается на ведение сеанса пользователя.
Итак, кажется, я просмотрел это. Изменение учетных данных, чтобы разрешить обмен файлами cookie / сеансами между клиентом и сервером, решило проблему.
Разделпредостережений документации
fetchгласит:По умолчанию fetch не отправляет и не получает никаких файлов cookie с сервера, что приводит к несанкционированным запросам, если сайт полагается на поддержание сеанса пользователя.
Рекомендуется использоватьAJAX для обмена информацией с видами Колб.
Между тем, в вашем коде для приложения Flask объектsessionявляется словарем. Теперь, если вы получаете доступ к словарю с его ключомsession['hello']и если этот ключ не существует, то возникаетKeyerror. Чтобы обойти эту ошибку, вы можете использовать методget()для словарей.Происходит следующее: запрос
fetchне находит ключhello(или не получает значение сеанса из представления колбы) в сеансе колбы.Но это все равно даст вам значениеuser = session.get('hello') return jsonify(session_info=user)nullдля сеанса{ session_info: null }. Почему это так?Когда вы отправляете запросы GET / POST на сервер Flask, сеанс инициализируется и запрашивается изнутри Колба. Однако при отправке запроса POST Javascript
fetchнеобходимо сначала получить значение сеанса из Flask, а затем отправить его в виде запроса POST в представление Flask, котороеreturnсодержит информацию о сеансе.В вашем коде, когда запрос POST запускается из
fetch, Когда я отправляю данные полезной нагрузки в Flask, они принимаются правильно, и вы проверяете это с помощьюrequest.get_json()в представлении Flask:@app.route('/update', methods=['POST']) def update(): user = session.get('hello') payload = request.get_json() return jsonify(session_info=user, payload=payload)Это вернет
{ payload: 'arbitrary_string', session_info: null }. Это также показывает, чтоfetchне получает сеанс информация, потому что мы не вызывали GET first, чтобы получить информацию о сеансе от Flask.Помните : сеанс Flask живет на сервере Flask. Чтобы отправлять / получать информацию через Javascript, вы должны совершать индивидуальные звонки, если только не предусмотрено сохранение файлов cookie сеанса.
const fetch = require('node-fetch'); var url_get = 'http://my_server_ip'; var url_post = 'http://my_server_ip/update'; fetch(url_get, { method:'GET' }).then((response)=>response.json()).then((data) =>fetch(url_post, { method: 'POST', body: JSON.stringify(data), dataType:'json', headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then((postdata) => { console.log(postdata); }));Вид колбы немного изменится:
@app.route('/', methods=['GET']) def set_session(): session['hello'] = 'world' return jsonify(session['hello']) @app.route('/update', methods=['POST']) def update(): payload = request.get_json() return jsonify(session_info=payload)Когда вы запускаете запрос Javacript сейчас, вывод будет:
{ session_info: 'world' }
Comments