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

sam*_*sam 5 javascript python session json 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')
Run Code Online (Sandbox Code Playgroud)

利用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);
})
Run Code Online (Sandbox Code Playgroud)

这里出了什么问题?为什么我可以使用Postman进行GET/POST请求,但遇到错误,使用Javascript发出相同的请求?

ama*_*anb 6

文档的警告部分fetch说:

默认情况下,fetch 不会从服务器发送或接收任何 cookie,如果站点依赖于维护用户会话,则会导致未经身份验证的请求。

建议使用AJAX与 Flask 视图交换信息。

同时,在 Flask 应用程序的代码中,session对象是一个字典。现在,如果您使用其键访问字典,session['hello']并且该键不存在,Keyerror则会引发 a 。要解决此错误,您可以使用get()字典的方法。

发生的情况是:fetch请求hello在 Flask 会话中找不到密钥(或从 Flask 视图中获取会话值)。

user = session.get('hello')
return jsonify(session_info=user)
Run Code Online (Sandbox Code Playgroud)

但这仍然会给你一个nullsession的值{ session_info: null }。为什么呢?

当你向 Flask 服务器发送 GET/POST 请求时,会话被初始化并从 Flask 内部查询。但是,当您发送 Javascript fetchPOST 请求时,您必须首先从 Flask 获取会话值,然后将其作为 POST 请求发送到return包含会话信息的Flask 视图。

在您的代码中,当 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)
Run Code Online (Sandbox Code Playgroud)

这将返回{ 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);
}));
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

当您现在触发 Javacript 请求时,输出将是: { session_info: 'world' }


sam*_*sam 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);
})
Run Code Online (Sandbox Code Playgroud)

根据Mozilla的Fetch使用指南

默认情况下,如果站点依赖于维护用户会话,则访存将不会从服务器发送或接收任何cookie,从而导致未经身份验证的请求。

所以看来我看了看这个。更改凭据以允许客户端和服务器之间的cookie /会话通信可以解决此问题。