400 Bad Request while POSTing json to Flask on openshift - python

I have read all related SO questions on this but I still keep encountering this error(400 - Bad Request) when posting json to my Flask app running on RedHat's openshift cloud platform.
This is my code:
flaskapp.py
import os
from datetime import datetime
from flask import Flask, request, flash, url_for, redirect, \
render_template, abort, send_from_directory
app = Flask(__name__)
app.config.from_pyfile('flaskapp.cfg')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/<path:resource>')
def serveStaticResource(resource):
return send_from_directory('static/', resource)
#app.route("/test")
def test():
return "<strong>It's Alive!</strong>"
#app.route('/mine', methods=['POST'])
def mine():
content = request.get_json(force=True)
print content
return "Success!\n"
if __name__ == '__main__':
app.run(debug=True)
This is how my app.py looks like
#!/usr/bin/env python
# This file may be used instead of Apache mod_wsgi to run your python
# web application in a different framework. A few examples are
# provided (cherrypi, gevent), but this file may be altered to run
# whatever framework is desired - or a completely customized service.
#
import imp
import os
import sys
try:
virtenv = os.path.join(os.environ.get('OPENSHIFT_PYTHON_DIR','.'), 'virtenv')
python_version = "python"+str(sys.version_info[0])+"."+str(sys.version_info[1])
os.environ['PYTHON_EGG_CACHE'] = os.path.join(virtenv, 'lib', python_version, 'site-packages')
virtualenv = os.path.join(virtenv, 'bin','activate_this.py')
if(sys.version_info[0] < 3):
execfile(virtualenv, dict(__file__=virtualenv))
else:
exec(open(virtualenv).read(), dict(__file__=virtualenv))
except IOError:
pass
#
# IMPORTANT: Put any additional includes below this line. If placed above this
# line, it's possible required libraries won't be in your searchable path
#
#
# main():
#
if __name__ == '__main__':
application = imp.load_source('app', 'flaskapp.py')
port = application.app.config['PORT']
ip = application.app.config['IP']
app_name = application.app.config['APP_NAME']
host_name = application.app.config['HOST_NAME']
fwtype="wsgiref"
for fw in ("gevent", "cherrypy", "flask"):
try:
imp.find_module(fw)
fwtype = fw
except ImportError:
pass
print('Starting WSGIServer type %s on %s:%d ... ' % (fwtype, ip, port))
if fwtype == "gevent":
from gevent.pywsgi import WSGIServer
WSGIServer((ip, port), application.app).serve_forever()
elif fwtype == "cherrypy":
from cherrypy import wsgiserver
server = wsgiserver.CherryPyWSGIServer(
(ip, port), application.app, server_name=host_name)
server.start()
elif fwtype == "flask":
from flask import Flask
server = Flask(__name__)
server.wsgi_app = application.app
server.run(host=ip, port=port)
else:
from wsgiref.simple_server import make_server
make_server(ip, port, application.app).serve_forever()
And this how am posting the data:
curl -X POST -H "application/json" -d '{"key":"val"}'
https://python-bonga.rhcloud.com/mine
N/B: This works fine on localhost

I found that a POST using curl without specifying the Content-Type defaults to sending Content-Type application/x-www-form-urlencoded
http://curl.haxx.se/docs/httpscripting.html#POST
And if the (different) mimetype exists in Flask, then the json data becomes unavailable; hence request.get_json(force=True) fails.
I therefore changed my code to look for form-data first before anything else
if request.form:
content = [item for item in request.form]
print "Content:", ''.join(content)
else:
content = request.get_json(force=True)
print "Content:", content

Related

Python Code on my XAMPP Website won´t work

from flask import Flask, render_template, request
import random
import datetime
app = Flask(__name__)
# List to store the submitted URLs
urls = []
#app.route('/')
def index():
return render_template('index.html')
#app.route('/submit', methods=['POST'])
def submit():
url = request.form['url']
# Add the URL to the list and return a success message
urls.append(url)
return "URL submitted successfully!"
#app.route('/stream')
def stream():
# Select a random URL from the last 20 days
now = datetime.datetime.now()
twenty_days_ago = now - datetime.timedelta(days=20)
recent_urls = [url for url in urls if url.submission_time > twenty_days_ago]
current_song_url = random.choice(recent_urls)
return render_template('stream.html', url=current_song_url)
if __name__ == '__main__':
app.run(debug=True)
I want to use this Code for my XAMPP Website (Html/php mostly used) but it only shows the code. So I watched some tutorials with config stuff and all that but then there is an internal server error. What should I do?
I tried to config Apache (httpd.conf) and installed everything (Python, Flask etc.)

Flask "send_from_directory" works in local, but doesn't in remote

On my flask app, I upload a file, save it to a directory and then send it back as attachment.
When I run my code on "debug" mode on my local machine using VSCode, it works as expected. But when I upload the app to the remote server, I always get a "404" error.
Any idea of what I am missing?
#import Flask
from flask import Flask, request, send_from_directory
import json
import os
app = Flask(__name__)
UPLOAD_FOLDER_MR_PROPER = './tmp/mrProper/'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER_MR_PROPER
#app.route('/')
def index():
return 'index'
#app.route('/mrProper', methods=['POST'])
def handler_mrProper():
import shortuuid
uploaded_file = request.files['file']
tmp_name = shortuuid.uuid()
_, tmp_ext = uploaded_file.filename.rsplit(".", 1)
tmp_filename = tmp_name + "." + tmp_ext
if uploaded_file.filename != '':
uploaded_file.save(app.config['UPLOAD_FOLDER'] + tmp_filename)
sp = os.path.join(app.instance_path, app.config['UPLOAD_FOLDER'] )
# SOME IRRELEVANT PROCESSING
return send_from_directory( app.config['UPLOAD_FOLDER'],
tmp_filename,
mimetype = request.files["file"].content_type,
as_attachment = True,
attachment_filename = uploaded_file.filename,
cache_timeout=0)
if __name__ == '__main__':
app.run(debug=True)
.htaccess solved the problem.
Two issues here:
Route: Must be absolute path: UPLOAD_FOLDER_MR_PROPER = '/var/...../tmp/mrProper/'
.htaccess rewrite: Adding "/app/tmp/mrProper/" solved it.

Python Flask background threading is not working: code is not being executed

Context:
I have a function in my views command where some varaibales is being send through a background task script after a HTTP post on form. This background script handles a lot of API calls and is converting this to JSON that is gonna be converted Because this can take a long time (because of the way the API Calls) go before the next html page is rendered I had decided to make this one a background task with threading. Eventually if possible I would like a que to be added later on.
The problem:
But the thing is even though I clearly set it to a daemon thread and everything. The code is not being executed. I the Python Console log does not even show the code being executed, so what I am doing wrong here.
Just look at Order 66 if you want the background task itself.
"""
Routes and views for the flask application.
"""
from datetime import datetime
from flask import render_template, jsonify
from FlaskWebPODTracer import app
import json
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from flask import request, redirect, url_for, flash
# The main script that has to happen in the background.
import classified.background_script.backgroundtask as Order66
from threading import Thread
import random
import string
from celery import Celery
from flask_mail import Mail, Message
import requests
API_VERSION = "v1/"
Succes_message = "U ontvangt nog een mail wanneer de data klaar is. Bedankt om PODTracer te gebruiken."
Fail_message_1 = "Wijzig de parameters van uw aanvraag en probeer opnieuw."
Fail_message_2 = "Blijft dit voorkomen contacteer dan support."
Fail_message = Fail_message_1 + '\n' + Fail_message_2
#app.route('/handle_data/')
#app.route('/handle_data/', methods=['POST', 'GET'])
def handle_data():
#if request.method == 'GET':
# return render_template(
# "request-completed.html",
# title=
# "Aanvraag ingediend",
# objects = jsonobjects,
# year=datetime.now().year,
# message='Uw aanvraag is voltooid.'
# )
if request.method == 'POST':
email = request.form['inputEmail']
stringpart = request.form['city']
sper_year = int(request.form["Speryear"])
urlpart = request.form["api-url-input"]
url = "Classified" + API_VERSION + urlpart
print(url)
response = requests.get(url)
if response.status_code == 200:
jsonobjects = len(response.json())
task = Thread(group=None, target=None,name=Order66.main, args=(stringpart,url,sper_year,email), daemon=True)
task.start()
state = "succesvol."
message_body = Succes_message
else:
jsonobjects = 0;
state = "onsuccesvol."
message_body = Fail_message
return render_template(
"request-posted.html",
respstate = state,
body = message_body,
title=
"Aanvraag ingediend",
objects = jsonobjects,
year=datetime.now().year,
message='Uw aanvraag is ' + state
)
# TODO RUN THIS IN BACKGROUND
# app.route('/request-completed')
#app.route('/handle_data_fail')
def handle_data_fail():
jsonobjects = 0
state = "onsuccesvol."
message_body = Fail_message
return render_template(
"request-posted.html",
respstate = state,
body = message_body,
title=
"Aanvraag ingediend",
objects = jsonobjects,
year=datetime.now().year,
message='Uw aanvraag is ' + state
)
As discussed in comments, here's an oversimplified example of an event-driven system with RabbitMQ and Flask.
Dependencies you need:
(flask-rabbitmq) ➜ flask-rabbitmq pip freeze
click==8.0.3
Flask==2.0.2
itsdangerous==2.0.1
Jinja2==3.0.3
MarkupSafe==2.0.1
pika==1.2.0
Werkzeug==2.0.2
Try to create a RabbitMQ docker container with the command below:
docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.9-management
your simple flask app would look like this:
from flask import Flask
from flask import request
import json
from RabbitMQPublisher import publish
app = Flask(__name__)
#app.route('/publish', methods=['POST'])
def index():
if request.method == 'POST':
publish(json.dumps(request.json))
return 'Done'
if __name__ == '__main__':
app.run()
your RabbitMQPublisher.py would be like this:
import pika
def publish(data: str):
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='0.0.0.0', port=5672))
channel = connection.channel()
channel.exchange_declare(exchange='test', exchange_type='fanout')
channel.queue_declare(queue='', exclusive=True)
channel.basic_publish(exchange='test', routing_key='', body='{"data": %s}'%(data))
connection.close()
and finally your script.py would be like this:
import pika
import json
from time import sleep
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='0.0.0.0', port=5672))
channel = connection.channel()
channel.exchange_declare(exchange='test', exchange_type='fanout')
result = channel.queue_declare(queue='', exclusive=True)
channel.queue_bind(exchange='test', queue='')
def callback(ch, method, properties, body):
body = json.loads(body.decode().replace("'", '"'))
print(body)
channel.basic_consume(
queue='', on_message_callback=callback, auto_ack=True)
channel.start_consuming()
inside the callback function in the code above, you can specify you're logic and when your logic finishes you can again use RabbitMQ to call a pattern on flask side or event do http call with requests. that would be your choice.

Flask - RuntimeError: No application found. Either work inside a view function or push an application context

I'm now trying to make an application that processes HTTP requests with REST APIs. The application uses Flask for the web framework, and it uses Celery for the asynchronous tasks.
Here's the application structure.
app.py
celery_app.py
controller/
controller.py
task/
task.py
(1) app.py
There are no lines for the Celery configuration.
from flask import Flask
...
app = Flask(__name__)
...
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
(2) celery.py
from consts.consts import Consts
from kombu.utils.url import quote
from celery import Celery
from config.config import Config
config = Config()
db_uri = 'db+' + config.get_db_uri()
aws_access_key = quote(config.get_config(Consts.AWS, Consts.AWS_ACCESS_KEY))
aws_secret_key = quote(config.get_config(Consts.AWS, Consts.AWS_SECRET_KEY))
broker_url = "sqs://{aws_access_key}:{aws_secret_key}#".format(
aws_access_key=aws_access_key, aws_secret_key=aws_secret_key
)
celery = Celery(
broker=broker_url,
backend=db_uri,
include=['task']
)
(3) task/task.py
I put all the tasks here.
from celery_app import celery
from flask import current_app as app
from model.model import db
#celery.task
def check_friend_status_from_db(user1, user2):
status = db.engine.execute(
"QUERY").fetchone()
return status
Now, the controller/controller.py file imports and calls the tasks as follows.
(4) controller/controller.py
from flask import Blueprint, request, json, render_template, jsonify
from mysql.connector import Error
from sqlalchemy import or_, and_
from consts.consts import StatusCode
from consts.consts import Consts
from model.model import db, FriendRequest
from outbound.request import Request
from util.logging import logger
from task.task import check_friend_status_from_db
controller_blueprint = Blueprint('controller_blueprint', __name__)
outbound_request = Request()
#controller_blueprint.route('/friend/status/<requested_from>/<requested_to>', methods=['GET'])
def check_friend_status(requested_from, requested_to):
logger.info('Checking the friend status')
try:
status = check_friend_status_from_db.apply_async((requested_from, requested_to)).get()
if status is None:
response = {
Consts.STATUS_CODE: StatusCode.OK,
Consts.FRIEND_STATUS: StatusCode.NO_RELATION
}
else:
response = {
Consts.STATUS_CODE: StatusCode.OK,
Consts.FRIEND_STATUS: status
}
except Error as e:
logger.error("TypeError:", e)
response = {
Consts.STATUS_CODE: StatusCode.ERROR
}
json_response = jsonify(response)
logger.info(json_response)
return json_response
When I run the code, I get the error as I mentioned on the title.
RuntimeError: No application found. Either work inside a view function or push an application context
and it turns out to be this part under the try block in the controller where the error is coming from.
status = check_friend_status_from_db.apply_async((requested_from, requested_to)).get()
Any solutions, please?
It looks like you need to register your blueprint controller_blueprint. Since this blueprint is not registered to your app, you are working outside the context of you application and hence the error.
You can do so in your app.py:
from controller.controller import controller_blueprint
app = Flask(__name__)
app.register_blueprint(controller_blueprint)

How to call microservice-2 from microservice-1 using python?

How to call my microservice-2 from microservice-1. So our result looks like this:-
Result :- {“message”: “vivek”} --> {“message”: “keviv”, “random”: 3.89}
command to access microservice-1:-
curl http://127.0.0.1:5000/reverse_random/vivek
microservice-1
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/reverse_reandom/<string:string>', methods=['GET'])
def reverse(string):
string = string[::-1]
return jsonify({'message': string })
if __name__ == '__main__':
app.run(debug = True)
microservice-2
import random
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/', methods=['GET'])
def myRandom():
r1 = random.uniform(0, 10)
return jsonify({'message': r1 })
if __name__ == '__main__':
app.run(debug=True)
you'll need to issue a GET request to service 2 in order to get the random number, I suggest to use requests for this, like
r = requests.get('url-for-service-2:port/')
data = r.json()
random_num = data['message']
keep in mind to check the data object for message key, or using .get() or equivalent
Run microservice-2 on a different port. Send request using Python standard or 3rd party library from microservice-1
to microservice-2 upon request to microservice-1.
Below is the example of using Python3 standard library only:
m1.py:
from flask import Flask, jsonify
import urllib
import json
app = Flask(__name__)
#app.route('/reverse_random/<string:string>', methods=['GET'])
def reverse(string):
content = urllib.request.urlopen('http://127.0.0.1:5001').read().decode('utf-8')
print('response from m2: ', content)
string = string[::-1]
return jsonify({'message': string, 'random' : json.loads(content)['message']})
if __name__ == '__main__':
app.run(debug = True)
m2.py:
import random
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/', methods=['GET'])
def myRandom():
r1 = random.uniform(0, 10)
return jsonify({'message': r1 })
if __name__ == '__main__':
app.run(debug=True, port=5001) # running m2 on a different port than default 5000
Run the m1: python3 m1.py
Run the m2 in a different shell: python3 m2.py
Send request to m1: curl http://127.0.0.1:5000/reverse_random/vivek
The result is:
{
"message": "keviv",
"random": 4.138115905045612
}
Observe the log of m1 and of m2 to make sure m2 was invoked.
To connect between services you can use background tasks such as celery and ramq or use nsq and nats

Categories

Resources