我正在开发一个Flask服务器,通过Web在一些后端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')
利用Postman,我可以向我的服务器发出GET请求并接收"world"
的预期输出。然后,我可以使用任意正文发出POST请求,并获得"world"
的预期输出(再次使用Postman)。
使用Chrome时,我可以访问我的服务器IP并在页面上查看预期的输出"world"
。我还可以使用Javascript(在Chrome控制台中)手动发出GET请求,并获得与预期相同的响应。但是,当尝试使用Javascript向服务器发送POST请求时出现问题;尝试发出此请求时,服务器显示KeyError: 'hello'
。
以下是我用来发出POST请求的Javascript:
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);
})
这里出了什么问题?为什么我可以使用Postman进行GET / POST请求,但遇到错误,使用Javascript发出相同的请求?
答案 0 :(得分:5)
经过几个小时的测试,我设法找出问题所在。虽然我认为@ amanb的答案突出了问题,但我将回答我自己的问题,因为我发现的最终是一个更简单的解决方案。
为了使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);
})
根据Mozilla's Fetch usage guide,
默认情况下,fetch不会从服务器发送或接收任何cookie, 如果站点依赖,则会导致未经身份验证的请求 维护用户会话。
所以看来我看了这个。更改凭据以允许客户端和服务器之间的cookie /会话通信解决了这个问题。
答案 1 :(得分:2)
fetch
文档的caveats部分说:
默认情况下,fetch不会从服务器发送或接收任何cookie,如果站点依赖于维护用户会话,则会导致未经身份验证的请求。
建议使用AJAX与Flask视图交换信息。
同时,在Flask应用程序的代码中,session
对象是一个字典。现在,如果您使用密钥session['hello']
访问字典,并且此密钥不存在,则会引发Keyerror
。要解决此错误,可以使用get()
方法进行词典。
正在发生的事情是:fetch
请求在Flask会话中找不到hello
密钥(或从Flask视图中获取会话值)。
user = session.get('hello')
return jsonify(session_info=user)
但是,这仍会为会话null
提供{ session_info: null }
值。为什么会这样?
当您向Flask服务器发送GET / POST请求时,会在Flask中初始化并查询会话。但是,当您发送Javascript fetch
POST请求时,您必须首先从Flask获取会话值,然后将其作为POST请求发送到Flask视图return
会话信息。
在您的代码中,当从fetch
触发POST请求时,当我将有效负载数据发送到Flask时,它会被正确接收,您可以使用Flask视图中的request.get_json()
进行检查:
@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来从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);
}));
Flask视图会略有改变:
@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' }