烧瓶会话不坚持(邮差工作,Javascript没有)

时间:2018-04-07 22:02:45

标签: javascript python json session flask

我正在开发一个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发出相同的请求?

2 个答案:

答案 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' }