Flask Session Not Persisting (Postman works, Javascript doesn't)

前端 未结 2 1995
走了就别回头了
走了就别回头了 2021-02-09 17:28

I\'m developing a Flask server to communicate between some backend Python functionality and Javascript clients over the web. I\'m attempting to utilize Flask\'s session

相关标签:
2条回答
  • 2021-02-09 17:44

    After a few hours of testing, I managed to figure out the issue. Although I think @amanb's answer highlights the problem, I'm going to answer my own question because what I found is ultimately a simpler solution.

    In order to make the POST request return the expected value, I simply needed to add a credentials: 'same-origin' line to the fetch body. This looks like the following:

    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);
    })
    

    According to Mozilla's Fetch usage guide,

    By default, fetch won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session.

    So it seems I looked over this. Changing the credentials to allow communication of the cookie/session between client and server resolved the issue.

    0 讨论(0)
  • 2021-02-09 17:55

    The caveats section of the fetch documentation says:

    By default, fetch won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session.

    It is recommended to use AJAX to exchange information with Flask views.

    Meanwhile, in your code for the Flask app, the session object is a dictionary. Now, if you access a dictionary with its key session['hello'] and if this key does not exist, a Keyerror is raised. To get around this error, you can use the get() method for dictionaries.

    What is happening is: the fetch request does not find the hello key(or GET the session value from the Flask view) in the Flask session.

    user = session.get('hello')
    return jsonify(session_info=user)
    

    But this will still give you a null value for the session { session_info: null }. Why is that so?

    When you send GET/POST requests to the Flask server, the session is initialized and queried from within Flask. However, when you send a Javascript fetch POST request, you must first GET the session value from Flask and then send it as a POST request to your Flask view which returns the session information.

    In your code, when the POST request is triggered from fetch, when I send the payload data to Flask, it is received correctly and you check this using request.get_json() in the Flask view:

    @app.route('/update', methods=['POST'])
    def update():
      user = session.get('hello')
      payload = request.get_json()
      return jsonify(session_info=user, payload=payload)
    

    This will return { payload: 'arbitrary_string', session_info: null }. This also shows that fetch does not receive the session information because we did not call GET first to get the session information from Flask.

    Remember: The Flask session lives on the Flask server. To send/receive information through Javascript you must make individual calls unless there is a provision to store session cookies.

    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);
    }));
    

    The Flask views will change slightly:

    @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)
    

    When you trigger the Javacript request now, the output will be: { session_info: 'world' }

    0 讨论(0)
提交回复
热议问题