I am posting data to the server and process the data on the server. Since the page reload takes a while, I would like to send status updates to the client and display them in a div. Things like "first file being processed...", "first file done", "second file being processed"..., and so on.
My first attempt:
#app.route('/', methods=["GET", 'POST'])
def main():
if request.method == 'POST':
socketio.emit("message", {'data': 42})
_urls = request.form['url_area'].split("\n")
lang = request.form['lang']
image_list = get_images(_urls, lang)
return render_template('index.html', images=image_list)
return render_template('index.html')
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('connect', function(msg) {
console.log("connected");
});
socket.on('message', function(msg) {
console.log(msg);
});
</script>
The client connects correctly. However, no message ever gets from the server to the client.
How do I need to do this?
I've looked at some examples but cannot figure out what I am doing differently. Many examples just show how to send data that the client sent, but I don't want that (right now). For now, I'd just like to send messages from the server.
EDIT
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
if __name__ == '__main__':
socketio.run(app)
Related
Here is what I am trying to do
I have a Flask app that is responding to clients requests
The same is also running some jobs to collect information from various sources
This is done by using ApScheduler and BackgroundScheduler
I would like that at the end of the job to send the webclient an update with the results of the job
Below is the code but it is not really working.
Here is the server side
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'secret-key'
socketio = SocketIO(app)
def get_updates(socketio):
socketio.emit('updates', {'updates': "this is the updates"})
return
if __name__ == '__main__':
if not app.debug or os.environ.get('WERKZEUG_RUN_MAIN') == 'true': #prevents scheduling the job twice -When in debug mode this runs twice
scheduler = BackgroundScheduler()
scheduler.add_job(func=get_updates, trigger="interval", seconds=30, kwargs={"socketio":socketio})
scheduler.start()
socketio.run(app,host='0.0.0.0',port=5001)
Browser side
<html>
<div>
<ul id="messages" style="list-style-type:none;" ></ul>
<form id="chat" action="">
<input id="input" autocomplete="off" /><button>Send</button>
</form>
</div>
<script>
var socket = io();
socket.on('connect', function() {
socket.send('Hello world');
});
var messages = document.getElementById('messages');
var form = document.getElementById('chat');
var input = document.getElementById('input');
form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('message', input.value);
input.value = '';
}
});
socket.on('updates', function(msg) {
console.log("Updates received")
console.log(msg)
var item = document.createElement('li');
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
</script>
</html>
Update: While reading as much as I can about his I was watching the console and I noticed that this worked once in like 50 exectutions of the scheduled job
For whoever is wasting a day of reading to solve this here is the solution
Add these lines before any imports related to flask or apscheduler
import eventlet
eventlet.monkey_patch(thread=True, time=True)
See this and this and also the docs for eventlet regarding the monkey patching
Used the following in cmd:
set FLASK_APP=hello_app.py
flask run --host=0.0.0.0
code:
'''
<input id="name-input" type="text"/>
<button id="name-button">Submit</button>
<p id="greeting"></p>
<script href="http://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$('#name-button').click(function(event){
let message = {
name: $('#name-input').val()
}
$.post('http://192.168.1.106:5000/hello', JSON.stringify(message), function(response){
$('#greeting').text(response.greeting);
console.log(response);
});
});
</script>
'''
from flask import Flask
from flask import jsonify
from flask import request
app = Flask(__name__)
#app.route('/hello', methods=['POST'])
def hello():
message = request.get_json(force=True)
name = message['name']
response = {
'greeting':'Hello,' + name + '!'
}
return jsonify(response)
if '__name__' == '__main__':
app.run(debug=True)'''
Got 405 error while running. Already tried adding 'GET' to the methods which led to Bad Request instead.
Can anybody please help?
Change the methods to ['GET','POST']
#app.route("/home",methods=['GET','POST'])
THEN handle the post data by
if request.method=='POST':
#handle post data here
else:
#return a normal page
return "home"
also change the last if 'name' == 'main': to this
if __name__ == '__main__':
EDIT:
add your jsonify code in the handle post data section
I am trying to write a small restful api application, i am using Chrome Postman extension for sending requests to the app .
I believe that my code does not have mistakes but every time i am sending post request a 400 Bad Request error raising , here is my code:
#api_route.route('/api', methods=['GET'])
def api():
return jsonify({'message':'Api v1.0'})
#api_route.route('/api', methods=['POST'])
def create_user():
data = request.get_json()
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message', 'New User Created!'})
The json data that i am sending looks like this:
{"email" : "Test", "password" : "123123123"}
Why i am getting the 400 error ??
Update:
Screenshots for the requests using Postman:
GET Request
POST Request
Here i am initiating api route inside api controller :
from flask import Blueprint
api_route = Blueprint(
'api',
__name__
)
from . import views
then i am registering it inside def create_app() function :
from .api import api_route
app.register_blueprint(api_route)
Here are the extensions that i am using in my application:
toolbar = DebugToolbarExtension()
assets_env = Environment()
cache = Cache()
moment = Moment()
htmlminify = HTMLMIN()
csrf = CSRFProtect()
jac = JAC()
googlemap = GoogleMaps()
session = Session()
principal = Principal()
I solved the problem, i've initiated CSRFProtect with app so i need to include X-CSRFToken in all my requests, so i have two choices:
1 - To include the csrf_token in request.headers for all the requests
2 - Using #csrf.exempt decorator that coming with flask_wtf.csrf
For now i am using #csrf.exempt, so it become like this:
#api_route.route('/api', methods=['GET','POST'])
#csrf.exempt
def create_user():
if request.method == 'GET':
return jsonify({'message' : 'API v1.0'})
elif request.method == 'POST':
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user_api = Api(email=data['email'], password=hashed_password)
db.session.add(new_user_api)
db.session.commit()
return jsonify({'message' : 'New user created!'})
return return jsonify({'message' : 'No user has been added!'})
Thanks for #MrPyCharm for his interests , salute :) .
A good approach would be to structure your views as follows:
Instead of creating view with same route for different request methods, you can handle the request methods in the same view:
#api_route.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'GET':
return jsonify({'message':'Api v1.0'})
else:
data = request.get_json(force=True)
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message': 'New User Created!'})
# Just in case the if condition didn't satisfy
return None
A note for anyone else experiencing this with PostMan and Flask - you will also hit a HTTP 404 if your URL in PostMan is HTTPS but your Flask app only handles HTTP.
Overall, my goal is to convert several console-based Python scripts into Flask, so they can be deployed as webapps.
Current challenge: converting a series of console print() messages into a streaming update in the HTML page.
It seems like these messages could be appended to a single string array, that is returned and shown on index.html. But there needs to be a way to trigger the HTML to update from the server side, since the user isn't going to supply any input.
I cobbled together some scripts from Streaming data with Python and Flask and Display data streamed from a Flask view as it updates. It doesn't show the messages, though, just "nothing received yet."
app.py:
from flask import Flask, Response, jsonify, render_template, request, redirect, url_for
import time
from time import sleep
app = Flask(__name__)
#app.route('/')
def index():
if request.headers.get('accept') == 'text/event-stream':
messages = []
def script():
#a lot of code goes here
yield "data: Part A completed.\n\n"
#more code
sleep(10)
yield "data: Part B completed.\n\n"
#more code
sleep(10)
yield "data: Part C completed.\n\n"
return Response(script(), content_type='text/event-stream')
return redirect(url_for('static', filename='index.html'))
if __name__ == '__main__':
app.run()
index.html:
<!doctype html>
<title>Messages</title>
<style>
#messages {
text-align: left;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
if (!!window.EventSource) {
var source = new EventSource('/');
source.onmessage = function(e) {
$("#messages").text(e.data);
}
}
</script>
<div id="messages">nothing received yet</div>
UPDATED to include Sergey's notes below
The point of error is that you try to send list of messages instead of text. You don't need to store messages in a list, you can just yield one message a time. Also the message consists of several fields and 'data' field is required:
#app.route('/')
def index():
if request.headers.get('accept') == 'text/event-stream':
def script():
#a lot of code goes here
yield "data: Part A completed.\n\n"
#more code
sleep(10)
yield "data: Part B completed.\n\n"
#more code
sleep(10)
yield "data: Part C completed.\n\n"
return Response(script(), content_type='text/event-stream')
return redirect(url_for('static', filename='index.html'))
To add messages to HTML page it should look like this:
<html>
<head>
<title>Messages</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
if (!!window.EventSource) {
var source = new EventSource('/');
source.onmessage = function(e) {
var m = $("#messages");
if (m.text() == 'nothing received yet')
m.html("<div>" + e.data + "</div>");
else
m.append("<div>" + e.data + "</div>");
}
}
</script>
</head>
<body>
<div id="messages">nothing received yet</div>
</body>
</html>
Background
The purpose of this project is to create a SMS based kill switch for a program I have running locally. The plan is to create web socket connection between the local program and an app hosted on Heroku. Using Twilio, receiving and SMS will trigger a POST request to this app. If it comes from a number on my whitelist, the application should send a command to the local program to shut down.
Problem
What can I do to find a reference to the namespace so that I can broadcast a message to all connected clients from a POST request?
Right now I am simply creating a new web socket client, connecting it and sending the message, because I can't seem to figure out how to get access to the namespace object in a way that I can call an emit or broadcast.
Server Code
from gevent import monkey
from flask import Flask, Response, render_template, request
from socketio import socketio_manage
from socketio.namespace import BaseNamespace
from socketio.mixins import BroadcastMixin
from time import time
import twilio.twiml
from socketIO_client import SocketIO #only necessary because of the hack solution
import socketIO_client
monkey.patch_all()
application = Flask(__name__)
application.debug = True
application.config['PORT'] = 5000
# White list
callers = {
"+15555555555": "John Smith"
}
# Part of 'hack' solution
stop_namespace = None
socketIO = None
# Part of 'hack' solution
def on_connect(*args):
global stop_namespace
stop_namespace = socketIO.define(StopNamespace, '/chat')
# Part of 'hack' solution
class StopNamespace(socketIO_client.BaseNamespace):
def on_connect(self):
self.emit("join", 'server#email.com')
print '[Connected]'
class ChatNamespace(BaseNamespace, BroadcastMixin):
stats = {
"people" : []
}
def initialize(self):
self.logger = application.logger
self.log("Socketio session started")
def log(self, message):
self.logger.info("[{0}] {1}".format(self.socket.sessid, message))
def report_stats(self):
self.broadcast_event("stats",self.stats)
def recv_connect(self):
self.log("New connection")
def recv_disconnect(self):
self.log("Client disconnected")
if self.session.has_key("email"):
email = self.session['email']
self.broadcast_event_not_me("debug", "%s left" % email)
self.stats["people"] = filter(lambda e : e != email, self.stats["people"])
self.report_stats()
def on_join(self, email):
self.log("%s joined chat" % email)
self.session['email'] = email
if not email in self.stats["people"]:
self.stats["people"].append(email)
self.report_stats()
return True, email
def on_message(self, message):
message_data = {
"sender" : self.session["email"],
"content" : message,
"sent" : time()*1000 #ms
}
self.broadcast_event_not_me("message",{ "sender" : self.session["email"], "content" : message})
return True, message_data
#application.route('/stop', methods=['GET', 'POST'])
def stop():
'''Right here SHOULD simply be Namespace.broadcast("stop") or something.'''
global socketIO
if socketIO == None or not socketIO.connected:
socketIO = SocketIO('http://0.0.0.0:5000')
socketIO.on('connect', on_connect)
global stop_namespace
if stop_namespace == None:
stop_namespace = socketIO.define(StopNamespace, '/chat')
stop_namespace.emit("join", 'server#bayhill.com')
stop_namespace.emit('message', 'STOP')
return "Stop being processed."
#application.route('/', methods=['GET'])
def landing():
return "This is Stop App"
#application.route('/socket.io/<path:remaining>')
def socketio(remaining):
try:
socketio_manage(request.environ, {'/chat': ChatNamespace}, request)
except:
application.logger.error("Exception while handling socketio connection",
exc_info=True)
return Response()
I borrowed code heavily from this project chatzilla which is admittedly pretty different because I am not really working with a browser.
Perhaps Socketio was a bad choice for web sockets and I should have used Tornado, but this seemed like it would work well and this set up helped me easily separate the REST and web socket pieces
I just use Flask-SocketIO for that.
from gevent import monkey
monkey.patch_all()
from flask import Flask
from flask.ext.socketio import SocketIO
app = Flask(__name__)
socketio = SocketIO(app)
#app.route('/trigger')
def trigger():
socketio.emit('response',
{'data': 'someone triggered me'},
namespace='/global')
return 'message sent via websocket'
if __name__ == '__main__':
socketio.run(app)