I have a Flask application containing various images displaying some clusters constructed by a K-Means algorithm.
The user should be able to select one or several images and afterwards get redirected to a web-page where the clusters can be further assessed.
The application worked as intended until I added basic access authentication.
Specifically, my (simplified) python script goes as follows:
from flask import Flask, render_template, request, redirect, url_for
from flask_cors import CORS
from flask_basicauth import BasicAuth
app=Flask(__name__)
app.config['BASIC_AUTH_USERNAME'] = os.environ['AUTH_USERNAME']
app.config['BASIC_AUTH_PASSWORD'] = os.environ['AUTH_PASSWORD']
app.config['BASIC_AUTH_FORCE']= True
basic_auth=BasicAuth(app)
CORS(app)
#app.route('/')
def show_index():
images=os.listdir(app.root_path+"/static/img/")
return render_template("cluster_images.html", cluster_images = images)
#app.route("/filter", methods=["POST", "GET"])
def filter():
if request.method=="POST":
sel=request.get_json()
#Do some operations with sel
The problem appears when I try to redirect to the /filter route from the main route which is done by "btn1".
The following shows the function from "cluster_images.html" taking care the http request when btn1 is fired
$("document").ready(function() {
$("#btn1").click(function(){
$.ajax({
type: 'POST',
beforeSend: function(){
$('.loader')},
contentType: 'application/json',
url: "http://localhost:5000/filter",
dataType : 'html',
data : JSON.stringify(images), success: function(){window.location.href = "http://localhost:5000/filter"}
})
})
})
When I press the button I get the following error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/filter. (Reason: CORS preflight channel did not succeed).[Learn More]
Related
I'm trying to store JWT Tokens in cookies for a Flask application to restrict some endpoints. An endpoint, "/authorize" , is responsible for setting the cookies then redirect the page to the root endpoint, "/".
from flask import Flask, request, make_response, redirect
#app.route("/authorize", methods=["GET"])
def authorize():
token = request.args.get('token')
expires = request.args.get('expires')
# some code to validate the token
resp_output = make_response(redirect("/"))
resp_output.set_cookie("token", token, expires=expires)
return resp_output
#app.route("/", methods=["GET"])
def index():
token = request.cookies.get("token)
# do something with the token
However, when I tried to deploy this, I ran into some problems with the redirecting and therefore have to change redirect("/") to redirect("https://someaddress.com/)" where https://someaddress.com/ is the address of the flask application. Now when I try to retrieve the token cookies in the root endpoint, it returns None. I suspect it is because the redirection has turnt from an internal one to an external one.
Please help me find a workaround for this. Or if you think I should resolve the problems that lead to the change from internal to external redirection so I can go back to what works. (If anyone can point me to some resources explaining exactly how redirection, or more specifically Flask's redirection, works, I'd really appreciate it.)
Using url_for function from flask should work in your case, as it will look for the link within the app context:
from flask import Flask, request, make_response, redirect, url_for
#app.route("/authorize", methods=["GET"])
def authorize():
token = request.args.get('token')
expires = request.args.get('expires')
# some code to validate the token
resp_output = make_response(redirect(url_for('index')))
resp_output.set_cookie("token", token, expires=expires)
return resp_output
#app.route("/", methods=["GET"])
def index():
token = request.cookies.get("token)
# do something with the token
Btw, I would recommend you pass your authorization logic to a decorator, have a look on authorization decorators using flask.
In case this don't work in production, that can be some setting related to your reverse proxy - like nginx conf file. Let me know if it is the case
on Nginx file on sites-enabled folder etc/nginx/sites-enabled/<project-name>, comment or remove the following line:
proxy_set_header Host $host;
Hope it suits you well!
I hope my question is clear enough.
So, I have a React form in component A. I want to pass the fields using an AJAX request to a Flask server, which processes the data received and updates the DOM in component B.
Tried looking up several other SO pages but none of them answered the question. And I'm super new to both AJAX and Flask, so that doesn't help either.
My current code looks like this:
Component A:
import React from "react";
class InputForm extends React.Component {
claimRef = React.createRef();
handleSubmit = event => {
event.preventDefault();
const claim = this.claimRef.current.value;
this.props.addClaim(claim);
$.ajax({
type: "POST",
url: "/test/",
data: claim
})
.done(function(data) {
// self.clearForm();
})
.fail(function(jqXhr) {
console.log("failed to register");
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Claim:
<textarea name="claim" ref={this.claimRef} placeholder="Claim" />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default InputForm;
Flask server:
#!/usr/bin/env python
import os
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route("/")
def index():
return render_template('index.html')
#app.route('/test/', methods=['GET', 'POST'])
def test():
clicked = None
if request.method == "POST":
clicked = request
return render_template('test.html', clicked=clicked)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=os.environ.get('PORT', 3000), debug=True)
I've added a test.html file temporarily, which is supposed to simply print the data, but even localhost:3000/test just prints "None".
I get absolutely no errors in any part of the application, and I also get status 200 in the network tab of the webpage, which means that the data is being accepted.
How do I access the passed data and subsequently, print it in component B?
There is nothing wrong with your reactjs http post, however I would recommend you to use the fetch api. However, when you want to talk to the client from your server you have to use json.
Here is how you would make an http request to the server try this:
const data = this.claimRef.current.value;
fetch('/test/', {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
.then(response => response.json())
.then(data => console.log(data));
Once you create make the http post to the server, this is how you retrieve the data from the server (flask)
#app.route('/test/', methods=['GET', 'POST'])
def test():
clicked = None
if request.method == "POST":
data = request.json
print(data) #json data from client (reactjs)
return jsonify(data='test')
# jsonify returns http response as json
Try it and see what you get! I hope this helps and good luck!
Be aware of CORS
fetch api
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Okay, I am struggling with this problem for 2 days now. When I am manually posting data in the form in the browser, everything works fine, and I am getting the flash message that should say 'Thanks...'. While testing my Flask application, this test does not pass because I am getting a 400 Bad Request error while sending a post request on my Flask form. To be clear, I am using Flask-Mail and WTForms for the form, and my application is dockerized and also running redis and celery. I am kinda new to these stuff, so if my question is not clear enough, please be kind and tell me if I should provide more detailed info. Thanks, and here is the relevant code and the error that is shown while testing with py.test. And sorry about the links, I am still not allowed to post pictures on StackOverflow.
The error code:
Pytest Assertion Error
contact/forms.py:
from flask_wtf import FlaskForm
from wtforms import TextAreaField, StringField
from wtforms.validators import DataRequired, Length, Email
class ContactForm(FlaskForm):
email = StringField("What's your e-mail address?",
[Email(), DataRequired(), Length(3, 254)])
message = TextAreaField("What's your question or issue?",
[DataRequired(), Length(1, 8192)])
contact/views.py:
from flask import (
Blueprint,
flash,
redirect,
request,
url_for,
render_template)
from flexio.blueprints.contact.forms import ContactForm
contact = Blueprint('contact', __name__, template_folder='templates')
#contact.route('/contact', methods=['GET', 'POST'])
def index():
form = ContactForm()
if form.validate_on_submit():
# This prevents circular imports.
from flexio.blueprints.contact.tasks import deliver_contact_email
deliver_contact_email(request.form.get('email'),
request.form.get('message'))
flash('Thanks, expect a response shortly.', 'success')
return redirect(url_for('contact.index'))
return render_template('contact/index.html', form=form)
contact/tasks.py:
from lib.flask_mailplus import send_template_message
from flexio.app import create_celery_app
celery = create_celery_app()
#celery.task()
def deliver_contact_email(email, message):
"""
Send a contact e-mail.
:param email: E-mail address of the visitor
:type user_id: str
:param message: E-mail message
:type user_id: str
:return: None
"""
ctx = {'email': email, 'message': message}
send_template_message(subject='[Flexio] Contact',
sender=email,
recipients=[celery.conf.get('MAIL_USERNAME')],
reply_to=email,
template='contact/mail/index', ctx=ctx)
return None
lib/tests.py:
def assert_status_with_message(status_code=200, response=None, message=None):
"""
Check to see if a message is contained within a response.
:param status_code: Status code that defaults to 200
:type status_code: int
:param response: Flask response
:type response: str
:param message: String to check for
:type message: str
:return: None
"""
assert response.status_code == status_code
assert message in str(response.data)
tests/contact/test_views.py:
from flask import url_for
from lib.tests import assert_status_with_message
class TestContact(object):
def test_contact_page(self, client):
""" Contact page should respond with a success 200. """
response = client.get(url_for('contact.index'))
assert response.status_code == 200
def test_contact_form(self, client):
""" Contact form should redirect with a message. """
form = {
'email': 'foo#bar.com',
'message': 'Test message from Flexio.'
}
response = client.post(url_for('contact.index'), data=form,
follow_redirects=True)
assert_status_with_message(200, response, 'Thanks')
Your browser will have requested the form with a GET request first, and thus have been given a CSRF token as a cookie and as a hidden form element in the form. When you then submit the form, the CSRF protection passes.
Your test doesn't make a GET request nor does it use the form fields from the form that such a request makes, so your POST request is missing both the cookie and the hidden field.
In a test, you could just disable CSRF protection by setting the WTF_CSRF_ENABLED parameter to False:
app.config['WTF_CSRF_ENABLED'] = False
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.