Wednesday, 1 May 2019

Flask post/redirect/get pattern cannot recover from an error

I have an error recovery problem in a Flask view function. Its simplified version is here:

@app.route('/log', methods=['POST', 'GET'])
def elog():
    form = LogForm()
    if form.validate_on_submit():
        flask.session['logformdata'] = form.data
        return flask.redirect(flask.url_for('elog'))

    try:
        formdata = flask.session.pop('logformdata')
    except KeyError:
        return flask.render_template('log.html', form=form)

    log = ... # result of a query
    form.process(data=formdata)
    return flask.render_template('log.html', form=form, log=log)

The important part is that it implements the post/redirect/get pattern and stores the form data between POST and GET in the flask.session storage which is implemented with HTTP cookies.

Now let's assume there is a bug somewhere and the function crashes with certain input. Of course, it should not happen. But when it does, the user's experience is terrible.

In detail:

  • the user posts a form (POST)
  • the form data is stored in the flask.session, i.e. as a cookie
  • after a redirect, the function is called again (GET), but now it crashes unexpectedly. The user sees some error message. That is not good, but bugs happen.
  • the user reloads the page intending to start over, but gets the same error repeated again and again!

The key point is that the statement flask.session.pop removes the form data from the session storage, but when the function crashes, the corresponding cookie remains in the user's browser. Each reload triggers the bug again. Restarting the browser may help (depending on session.permanent flag). The only guaranteed remedy is to manually delete the cookie from the browser. This effectively makes the webpage unusable.

I think I can mitigate the problem with setting a very short cookie lifetime (15 seconds or so) or by generating a new secret key after each restart. I did not try it and it is definitely not a good solution if session cookie contains other data.

How can I make functions like the one above more robust?



from Flask post/redirect/get pattern cannot recover from an error

No comments:

Post a Comment