Here is a code snippet that is a simple note application that takes in input from the user and displays the notes on the browser. The notes are stored in the session variable provided by flask. My question is that even though the server is running and the notes I entered are in the variable, why does the notes variable clear when I close the browser?
How can I make it persist even after the browser closes? Please let me know a solution with the use of session variable only.
from flask import Flask, render_template, request, session
from flask_session import Session
app = Flask(__name__)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
print(app.config)
notes = []
#app.route("/", methods=["GET", "POST"])
def index():
if session.get("notes") is None:
session["notes"]=[]
if request.method == "POST":
note = request.form.get("note")
session["notes"].append(note)
return render_template("index.html", notes=session["notes"])
Session ID's are generated and stored as a client cookie when the user starts communicating with the server. The sessions themselves are stored in the server (usually in memory, but this is implementation based).
Sessions are meant to be based on single user interaction with a web page. If you don't set a (sufficiently) long expiry for the cookie, it'll wipe the cookie when the browser is exited. Try setting a SET-COOKIE field on the HTTP response timeout.
Related
I'm currently building my very first own project, a web application with Flask which interacts with the Spotify API. The project is as good as ready after extensive testing locally and on a Heroku staging environment. However I'm currently experiencing a small login 'bug' I can't seem to wrap my head around.
When clicking a Login button, the application sends a request to the Spotify API, which sends back an authorization code upon confirmation of the user to read his data. This confirmation will result in the user being redirected to the '/profile route' of the web app. The flow of the login process so far on different environments:
Locally: process has always ran smoothly (read: click the Login button once which redirects the user to /profile route)
Staging (Heroku): clicking the login button generates the correct request to the Spotify API. However, when clicking it for the for the 1st time I get redirected to the login page (due to my login_required Flask decorator). When clicking it for the 2nd time it also generates the correct request and correctly sends the user to the '/profile route'.
I can see in the server logs clicking the login button for the 1st time generates the correct request. But it seems as if the validation by the login_required decorator of the '/profile route' goes 'too fast'? Or does this have something to do with caching? Because I'm also able to reproduce the bug (sometimes) by removing cache and hard refreshing the page.
I recently added a SECRET_KEY to session and changed the SESSION_PERMANENT from False to True but I don't think this is causing the issue? Some of the code I think might be causing this little bug:
# Ensure responses aren't cached
#app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
# Configure session to use filesystem
app.config['SECRET_KEY'] = os.urandom(64)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = True
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
def login_required(f):
"""
Decorate routes to require login:
http://flask.pocoo.org/docs/1.0/patterns/viewdecorators/
"""
#wraps(f)
def decorated_function(*args, **kwargs):
if session.get("authorization_header") is None:
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
#app.route("/")
def index():
""" Shows landing page """
if session.get("authorization_header") is not None:
return redirect(url_for('profile'))
return render_template("index.html")
#app.route("/login")
def login():
""" Shows login page """
if session.get("authorization_header") is not None:
return redirect(url_for('profile'))
return render_template("login.html")
#app.route("/logout")
#helpers.login_required
def logout():
""" Logs user out """
# Forget any user_id
session.clear()
# Redirect user to login form
return redirect(url_for("index"))
#app.route("/profile")
#helpers.login_required
def profile():
""" Shows overview of user's Spotfy profile """
Not sure if this is your issue, but could cause some weird behaviour...
app.config['SECRET_KEY'] = os.urandom(64) This will result in a different secret key being set on each heroku dyno (if running multiple workers; the default is 2 for syncronous workers) as that random string is generated dynamically at worker boot time.
As a side note, os.urandom isn't for generating keys. Instead use the secrets module, and set the resulting string as a config var in heroku CLI and load it in your app with:
import os
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
I successfully completed my web app with Flask ad deployed it on my server. I use current_user in some of my methods and I need that information for some purposes. Lately I noticed a problem. There was already logged in pages and at that point I restarted flask server. Logged in pages were untouched during that. And current_user became anonymous in all open and previously logged in pages. To prevent that, I tried such:
#app.before_request
def make_session_permanent():
session.permanent = True
But still the same problem What should I have done? At least I would like to force all client pages to go to login page after server restart.
with app.secret_key = os.urandom(12)I was using a changing secret key. I did it so that app.secret_key = 'sansal54' and it was solved.
I'm trying to send a post request to my Flask app from one of its own views, but it hangs until I kill the server. If I do the request in JavaScript, it works fine. Why does it not work from the Python code?
from flask import Blueprint, render_template, abort, request, Response, session, url_for
from jinja2 import TemplateNotFound
from flask.ext.wtf import Form
from wtforms import BooleanField, TextField, PasswordField
import requests
login = Blueprint('login', __name__, template_folder='templates')
class LoginForm(Form):
email = TextField('Email')
password = PasswordField('Password')
#login.route('/login', methods=['GET', 'POST'])
def _login():
form = LoginForm(request.form, csrf_enabled=False)
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
return render_template('login.html', form=form)
Prior to 1.0, Flask's development server was single-threaded by default. In that mode, it can only handle one request at a time. Making a request blocks until it receives the response. Your Flask code makes a request in the one thread, and then waits. There are no other threads to handle this second request. So the request never completes, and the original request waits forever.
Enable threads in the dev server to avoid the deadlock and fix the immediate problem.
app.run(threaded=True)
However, making a full HTTP request to the app from within the app should never be necessary and indicates a deeper design issue. For example, observe that the internal request will not have access to the session on the client's browser. Extract the common code and call it internally, rather than making a new request.
def common_login(data):
...
#app.route("/login")
def login():
...
common_login(data)
...
#app.route("/api/login")
def api_login():
...
common_login(data)
...
I'm not familiar with Flask. However this bit of code:
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
Seems like you're accepting a posted form, validating it, and then posting it again. Over and over.
I'm trying to send a post request to my Flask app from one of its own views, but it hangs until I kill the server. If I do the request in JavaScript, it works fine. Why does it not work from the Python code?
from flask import Blueprint, render_template, abort, request, Response, session, url_for
from jinja2 import TemplateNotFound
from flask.ext.wtf import Form
from wtforms import BooleanField, TextField, PasswordField
import requests
login = Blueprint('login', __name__, template_folder='templates')
class LoginForm(Form):
email = TextField('Email')
password = PasswordField('Password')
#login.route('/login', methods=['GET', 'POST'])
def _login():
form = LoginForm(request.form, csrf_enabled=False)
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
return render_template('login.html', form=form)
Prior to 1.0, Flask's development server was single-threaded by default. In that mode, it can only handle one request at a time. Making a request blocks until it receives the response. Your Flask code makes a request in the one thread, and then waits. There are no other threads to handle this second request. So the request never completes, and the original request waits forever.
Enable threads in the dev server to avoid the deadlock and fix the immediate problem.
app.run(threaded=True)
However, making a full HTTP request to the app from within the app should never be necessary and indicates a deeper design issue. For example, observe that the internal request will not have access to the session on the client's browser. Extract the common code and call it internally, rather than making a new request.
def common_login(data):
...
#app.route("/login")
def login():
...
common_login(data)
...
#app.route("/api/login")
def api_login():
...
common_login(data)
...
I'm not familiar with Flask. However this bit of code:
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
Seems like you're accepting a posted form, validating it, and then posting it again. Over and over.
I have a python flask app with login module implemented using extension python flask. In my login method, I have set below
def login():
.......
.......
session.permanent = True
app.permanent_session_lifetime = datetime.timedelta(minutes=3)
......
......
This code sets my session cookie to 3 minutes as expected.
What's happening right now is, after 3 minutes, the page is still active and sending GET requests and POST request even the session is expired. What I want is after session is expired the page to default to login page.
Does flask-login provides this functionality out of box? Any suggestion would be helpful
Thanks
Using flask-login, do this, it will automatically redirects to login page.
#app.before_request
def before_request():
flask.session.permanent = True
app.permanent_session_lifetime = datetime.timedelta(seconds=30)
flask.session.modified = True