I am working on a Flask web application, and encountering an issue concerning duplicate HTTP requests.
Every time a request is made, it is sent twice (for GET requests and POST requests as well), as can be seen on the screenshot below. I am running the application in production environment and with debug mode being "off".
How could I find out why the requests are being sent twice ?
EDIT:
The view functions are indeed being called twice every time a request is made. Any ideas ?
from flask import render_template
#product.route('/', methods=['GET', 'POST'])
#login_required
def index():
print('Request to index.')
products = Product.query.all()
return render_template('product/index.html', products=products)
Chrome sends an extra GET request for a favicon. Add this to the head of your html to stop this request
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
Related
Background:
I've built and deployed an app with Google Cloud Firebase. At a high level, I have a Python Flask server running on Cloud Run, and I serve static JS files with Firebase hosting.
Issue:
Sometimes, I want to redirect the user, but I'm sending them to the Cloud Run service URL rather than my app domain.
EDIT: I'm NOT experiencing this in the JS on the browser, but ONLY in the Python on the server.
Python
If a user navigates to a page without being signed in, e.g. following a link, they are redirected to my login page. For example, if someone who is not signed in tries to look at someone else's profile, the following code redirects them to the authentication blueprint's login endpoint:
if not session.get('user'):
return redirect(url_for('authentication.login'))
I would expect them to be redirected to my-app-name.web.app/auth/login but instead they're routed to my-cloudrun-service-name-XXXXX-region.run.app/auth/login. While the pages loaded look the same, they're really not. The second one redirects to the Cloud Run service URL, which doesn't have my static files served by Firebase Hosting.
I'm not really sure, but I believe this happens because Flask is generating the URL using the request context. So the request from the browser hits Cloud Run Load Balancer, which directs the request to my Cloud Run instance, but that means the Flask app in my Cloud Run instance only sees the internal Google Cloud redirect, and it doesn't know my domain.
I've tried solving this by setting app.config['SEVER_NAME'] = my-app-name.web.app, but I just get the "Not Found" page on each request. Is SEVER_NAME the solution but I'm not implementing it correctly, or is there another way to fix the Flask url_for generation?
I've found what I deem to be an effective solution / workaround.
I set the app config to store my BASE_URL when the app is created:
app.config['BASE_URL'] = 'https://my-url.domain'
Then I can access this as application context during requests, even from blueprints:
#blueprint.route('my_route/')
def my_route():
if not session.get('user'):
return redirect(current_app.config['BASE_URL'] + url_for('authentication.login', 302)
This has two key benefits for me:
There's only one place, the app config, to update if I change the domain
url_for is still used, so there's no hardcoding in case blueprints or routes change
I got question about implementing google login. I was able to implement Google Login button on my react app using an open source library called react-google-login. I was able to set up the backend server using python flask. I host my api method on the api on Heroku: http://arrangement-server.herokuapp.com/login.
And my react app runs successfully locally, and I am able to use login-in button. But, my issue is that my server displays the following error:
Method not Allowed.
Is this because it is post method?
Why is it my Server shows me that that method is not allowed?
Even though on the client side it runs fine, and I am able to see user profile and user information.
Here's the code to my backend server, you can find it at Github:
#app.route("/login", methods=['POST'])
def login():
data = request.json
session['access_token'] = data['access_token'], ''
return jsonify({'message':'You are logged in.'})
Your "login" endpoint will accept only "POST" HTTP requests. Because of this line:
#app.route("/login", methods=['POST'])
When you try to open your page in a browser - the browser will send the "GET" HTTP request to that URL.
That is why you are getting "Method Not Allowed" error.
Take a look at my answer on upwork for more details.
Your heroku server is only a backend server.
And the route "/login" accepts only POST request.
So you can't send the GET request to this route on web browser.
If you want to look at the response with this route, you can send the POST request by using POSTMAN.
I think this part
#app.route("/")
def home_page():
access_token = session.get('access_token')
if access_token is None:
return redirect(url_for('login'))
Always force user to visit login page with GET method. Unfortunately you don't have method and route defined to handle this GET method.
I have a simple flask application deployed using CGI+Apache running on a shared hosting server.
The app runs Flask 0.11.1 on Python 2.6 along with Flask-Mail 0.9.1.
One of the pages in the app has a contact form that sends an email and redirects back to the same page.
The contact form has a POST action to '/sendmail' that is defined in Flask controller as follows -
#app.route("/sendmail", methods=['GET','POST'])
def send_mail():
print "Sending Email"
mail = SendMail(app)
mail.send_mail(request.form['name'], request.form['mail'], request.form['phoneNo'], request.form['message'])
return render_template('contact.html')
Here's the issue -
With the code above, the app sends me an email successfully, however then gives an error '/sendmail' not found. The template fails to render.
If I remove the print statement from the snippet, the app renders contact.html successfully after sending the email.
What explains the behaviour of print statement in the snippet? Considering the execution is sequential, shouldn't the block fail at the print statement itself without sending the email instead of failing during rendering the template?
Print statement should not create an error as it is one of the statement like others. Instead since you are not checking for request.method=='POST', this should create and throw an error in your get request. To redirect to the same page return redirect("/sendmail") Do not forget to import from flask like this from flask import redirect
I'm writing an app using Flask.
I have a set of routes and they work.
What I want to do on the client side is to ignore any requests to invalid URLs. That is I do not want to render any 404/error pages in the app. I would like an alert that says the URL is invalid and for the browser to simply stay on the same page.
I don't want to be checking the URLs in JavaScript on the client, as this would expose them.
I have a route which responds correctly to unknown URLs:
#app.errorhandler(404)
def non_existant_route(error):
return jsonify({"no":"such page"})
If I delete the return statement I get a 500 error.
I can't use abort()
Does this idea violate some HTTP principle?
Thanks
It sounds like you need a "catch-all" endpoint. Typically, it seems a catch-all endpoint would return a generic 404, but in your case, you probably want to return a 200 with some contextual information. Here's basically how you can do it (credit goes to http://flask.pocoo.org/snippets/57/):
from flask import Flask
app = Flask(__name__)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def catch_all(path):
# returns a 200 (not a 404) with the following contents:
return 'your custom error content\n'
# This is just one of your other valid routes:
#app.route('/stuff')
def stuff():
return 'stuff\n'
if __name__ == '__main__':
app.run()
If you run this and curl various endpoints of the test app, here's what you get:
$ curl localhost:5000/stuff
stuff
$ curl localhost:5000/foo/bar
your custom error content
$ curl localhost:5000/otherstuff
your custom error content
As you can see, your other routes will still work as you expect.
I've decided a solution to this is too hard! I can not find any way to get the browser to ignore a response. There is no response header for 'do nothing'. If there was we would probably never see a webserver error again, which would not be good.
I could ajaxify all the requests as a way to grab the response headers and analyze them before any rendering or redirecting happens. That starts to break all the navigation (back buttons at least) and the pretty URLs. I could bung in a JS routing framework etc, and while I'm leaning how it works I'm not building my app (I already have enough to learn!)
#app.errorhandler(404)
def page_not_found(error):
return redirect(url_for('index'))
If you come up with something great post it anyway, I'm not the first to ask this question, and probably not the last.
Thanks
I remember reading about a javascript library some days ago (but I don't remember the name...). The clou with this library was, that all links and form submits were loaded not directly into the browser "_top" frame/window but into a hidden div and afterwards, when done, the content of the page was replaced by the content of this hidden div.
So if you want to catch bad links and such on client side you could hook up all links and submits and check the http response code. If it is not 200 (ok) you display an error. If it is okay you decide, if you replace the old page with the new content.
But there are two problems with this solution:
1. You would have to change the browsers location (in the address bar) without reloading the page of course!
2. It might get tricky to post some file uploads with javascript.
If I find the link or name of the js-library I saw, I will tell you!
I am trying to create a simple app where an array of integers is generated on the server and sent to the client. Here is some sample (working) code in app.py:
from flask import Flask, render_template, request, url_for
import random
app = Flask(__name__)
#app.route('/')
def form():
s_abc = [random.random() for _ in range(40)]
return render_template('abc.html', s_abc=s_abc)
if __name__ == '__main__':
app.run(debug=True)
And here is a (working) snippet of abc.html:
<div>
{{s_abc}}
</div>
The problem is, this code uses the GET HTTP method (which Flask uses by default when none is specified). I would like to instead use the POST HTTP method, since it is apparently more secure for sending data. (Source: http://blog.teamtreehouse.com/the-definitive-guide-to-get-vs-post)
To do this, I tried to change:
#app.route('/')
to:
#app.route('/', methods=['POST'])
Unfortunately, I got an error: "Method Not Allowed: The method is not allowed for the requested URL."
Question: How should I fix this?
[Note: From what I currently understand, I would need to create a form to use the POST method, but I don't think my website would need a form, since the client isn't sending data to the server. (In my website, the server is sending data to the client.)]
[Note: This is similar to a question I asked previously, but not the same, because here I am asking about a specific error message, Method not Allowed.]
You should not be using the POST method in this instance. The POST method is meant to be used when you are changing data on the server. In this case you are only retrieving data, so GET is the more appropriate method to use here.
In any case, if you do decide to use POST, your code is actually working. You can see this if you use a POST request to access the endpoint:
$ curl -X POST http://127.0.0.1:5000/
<div>
[0.03464541692036849, 0.5588700799957625, 0.4702806873145451, 0.7525198710149907, 0.0674801622743858, 0.28229897849445273, 0.17400190415782735, 0.48911931330821357, 0.8033543541248421, 0.16335301905982258, 0.3307436416488905, 0.11670066397858725, 0.552907551276049, 0.6699689958218984, 0.7444295210533091, 0.8258885497774587, 0.8656500198078877, 0.6826827672886756, 0.27219907080455874, 0.9053285546116574, 0.8328655798090894, 0.2323223157770763, 0.9775485685217323, 0.34887389370958166, 0.17092511319368353, 0.20875570398480459, 0.6744092445507751, 0.6176283706166301, 0.05070680888843082, 0.3441890248079591, 0.17701427714228501, 0.115329649057473, 0.325114272097177, 0.19386610624431766, 0.18892384889766745, 0.511139418514318, 0.019931284111035397, 0.5240369332606203, 0.8936272011794374, 0.9665936114223397]
</div>
If you just access the URL in your browser, this will actually send a GET request, and you have modified your code to specifically only allow the POST method - which is why you get the Method Not Allowed response.
$ curl -X GET http://127.0.0.1:5000/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>
The best solution is for you to use GET in this instance.
When you use the browser to visit a page, the browser always send 'GET' request to server... so when you've changed the methods to 'POST', flask can't find any GET route for '/' and return "Method Not Allowed" error, as the '/' doesn't allows GET anymore when browsers asks for that page.
You shouldn't use POST for this. POST only used when submitting data from forms or ajax.