I'm using WebSockets in the Tornado Framework and can't get the data in a html form to be sent to a tornado class.
This is my code:
class MainHandler(tornado.web.RequestHandler):
event = []
def get(self):
self.render('main.html')
def post(self):
MainHandler.event = self.get_argument('event')
When I try and send event to a WebSocketHandler class. no data is received from the form:
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print "tailing..."
db = Connection().blah
coll = db.blah_tail
event = MainHandler.event
print 'Filtered', event
'Filtered' just prints an empty list: "Filtered []".
The html form:
<form action="/" method="post">
<input type="text" name="event" />
<input type="submit" id="open" value="Submit Query" />
</form>
How could you send the form data to the WSHandler class?
Thanks
The js for creating the websocket:
<script>
$(document).ready(function() {
var ws;
$("#open").click(function(evt){
evt.preventDefault();
ws = new WebSocket("ws://" + "localhost" + ":" + "8888" + "/ws");
ws.onmessage = function(evt) $("#display").append(evt.data + "<br />");
ws.onclose = function(evt) {alert("Server connection terminated");};
});
});
</script>
Just like in the example from the Tornado documentation, I'll use a set for the WebSocket clients. Improving this is left as an exercise for the reader.
# clients listing on the WebSocket
clients = set()
class MainHandler(tornado.web.RequestHandler):
def get(self):
return self.render("index.html")
def post(self):
global clients
event = self.get_argument("event")
print "got event", event
if not clients:
print "No WebSockets, no point in querying the database"
return
for coordinate in self.get_coordinates(event):
for client in clients:
print "sending coordinate", coordinate, "to client", client
client.write_message(json.dumps(coordinate,
default=json_util.default))
def get_coordinates(self, event):
# replace with a real database query
for coordinate in ("No", "man's", "land"):
time.sleep(1)
yield coordinate
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
global clients
print "WebSocket opened..."
clients.add(self)
def on_close(self):
global clients
print "WebSocket closed..."
clients.remove(self)
The relevant part of the index.html template:
<script type="text/javascript">
$(document).ready(function() {
var ws;
// open WebSocket for getting the results
ws = new WebSocket("ws://" + location.host + "/ws");
ws.onmessage = function(evt) {
$("#display").append(evt.data + "<br>");
};
ws.onclose = function(evt) {alert("Server connection terminated");};
$("#open").click(function(evt) {
evt.preventDefault();
$.post("/", $("#eventForm").serialize());
});
});
</script>
</head>
<body>
<h1>Event follower</h1>
<h2>Enter the event you would like to follow</h2>
<form id="eventForm" action="/" method="post">
<input type="text" name="event" />
<input type="submit" id="open" value="Submit Query" />
</form>
<h2>Coordinates</h2>
<div id="display">
</div>
</body>
When the page is loaded, a WebSocket connection is made to the server to the WSHandler class and the client is added to the clients set. When the page is closed, the WebSocket connection is closed and the server will remove it from the set.
When the open submit button is clicked, the form will be submitted asynchronously using AJAX to MainHandler.post. The method will find out the coordinates related to that event and send them to the listening clients as they come it. The browser receives each coordinate and it appends it to the display div.
What is the handler of your function
MainHandler or WSHandler,
Only One of them call at a single time so your syntax
event = MainHandler.event won't produce any result for you.
If your objective is only to submit the form.
Then on Submit type of event you have to write a post or get function associated with your submit call in your JS, That will work with normal tornado.web.RequestHandler on server side.
Ref. tornado web socket chat example
I have updated the chat example :
$(document).ready(function() {
if (!window.console) window.console = {};
if (!window.console.log) window.console.log = function() {};
$("#messageform").live("submit", function() {
newMessage($(this));
return false;
});
$("#message").select();
}
});
function newMessage(form) {
var message = form.formToDict();
var disabled = form.find("input[type=submit]");
disabled.disable();
$.postJSON("URL", message, function(response) {
console.log(response);
});
}
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
if (callback) callback(eval("(" + response + ")"));
}, error: function(response) {
console.log("ERROR:", response)
}});
};
When you will call $("#message").submit() you will receive form data in you "URL" function
If you want's to use WSHandler then
Ref. example link will help you.
See if this helps.
Related
I just learn flask to use with jquery, just want to print out what i typed in , but it print nothing
here is html code
<body>
<input id="name-input" type="text" />
<button id="name-button">Submit Name</button>
<p id="greeting"></p>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#name-button").click(function (event) {
let message = {
name: $("#name-input").val()
}
$.post("http://10.0.0.4:5000/hello", JSON.stringify(message), function (response) {
$("#greeting").text(response.greeting);
console.log(response);
});
});
</script>
</body>
here is flask code:
from flask import request
from flask import jsonify
from flask import Flask
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)
when i click button, it print nothing!. please help, thank a lot
Two issues:
You're using the IP address shown in the example, which is very unlikely to point to your own PC on a network. More likely is 127.0.0.1:5000/hello, which would be localhost if you're running through the dev server. You can actually run the server across the network by providing the --host=0.0.0.0 flag to flask run. NOTE: This is only for the development server; you'll want to look at deployment options when you run this for real
Your route only accepts POST requests, but you're sending a GET request
The HTMl:
<body>
<input id="name-input" type="text" />
<button id="name-button">Submit Name</button>
<p id="greeting"></p>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#name-button").click(function (event) {
let message = {
name: $("#name-input").val()
}
$.post("http://127.0.0.1:5000/hello", JSON.stringify(message), function (response) {
$("#greeting").text(response.greeting);
console.log(response);
});
});
</script>
</body>
The route:
#app.route('/hello',methods=['GET', 'POST'])
def hello():
message = request.get_json(force=True)
name = message['name']
response = {
'greeting': 'Hello, ' + name + '!'
}
return jsonify(response)
I'm extremely new to React and Python and just trying to do a simple post from a react form to my Python API that will interface with a mongoDB.
I have a form in react that invokes a handleSubmit function on submit. I want the handleSubmit function to POST to my Python API running on port 5000. My react app is running on port 8080.
The handleSubmit looks like this:
handleSubmit(event) {
const axios = require('axios');
const baseUrl = 'http://localhost:5000'
axios.post('http://localhost:5000/api/create', JSON.stringify(params))
.end((error, response) => {
if (!error && response) {
console.log('got a valid response from the server')
} else {
console.log(`Error fetching data from the server: `, error)
}
});
event.preventDefault();
}
Python endpoint code:
#app.route('/api/create', methods=['POST'])
def create(self):
if request.method == 'POST':
print(request.args.get('exp_title'))
return True
return False
When I click the button, my python API endpoint isn't reached because react is trying to post to a route on port 8080. What am I missing?
I've tried using a regular ajax call and get the same result. At one point, I did something and got a CORS error in the browser, but I can't remember how I did that.
To enable cors, you need to install pip install -U flask-cors,
here is the website: https://flask-cors.readthedocs.io/en/latest/
or you can define cors in proxy in your reactjs package.json like here:
https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development
Once you install cors in your python app, try this:
Python app:
#app.route('/api/', methods=['POST', 'GET'])
def api_post():
if request.method == 'POST':
print('post app')
req = request.json
print(req)
return jsonify(name='john')
React app:
function App() {
const [todos, setTodos] = useState(null);
const [value, setValue] = useState('');
function handleSubmit(e) {
e.preventDefault();
const data = { name: value };
console.log('submit');
console.log(value);
fetch('http://127.0.0.1:5000/api/', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
})
.then(res => res.json())
.then(res => console.log(res));
}
function handleValue(e) {
setValue(e.target.value);
}
return (
<section id="app">
<form action="" onSubmit={handleSubmit}>
<input type="text" onChange={handleValue} />
<button> submit </button>
</form>
</section>
);
}
render(<App />, document.querySelector('#root'));
I have a basic chat that works fine, however when a user opens another page (/chat and /profile at same time) I get another connection, and same user will have duplicated sessions in the chat page.
What I am looking for a way to avoid this behavior. Maybe allow sockets only in /chat page?
main.py
....
#socketio.on('message')
def handleMessage(msg):
print ('Message: ' , msg)
send(msg, broadcast=True)
chart.html
var socket = io.connect('http://192.168.56.10/');
socket.on('connect', function() {
socket.send('User has connected!');
});
socket.on('message', function(msg) {
$("#messages_chat").append('<li>' + msg + '</li>');
console.log("receive messages");
});
$("#sendbutton").on('click', function() {
socket.send($('#myMessage').val());
$('#myMessage').val('');
});
views.py
....
#mod.route('/chat', methods=['GET'])
#login_required
def chat():
return render_template("users/chat.html")
I am trying to upload many files at once to my CherryPy server.
I am following this tutorial that shows PHP code on the server side.
The JavaScript part is simple. Here is a summary of what it does:
function FileSelectHandler(e) {
var files = e.target.files || e.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "upload", true);
xhr.setRequestHeader("X_FILENAME", file.name);
xhr.send(file);
}
I translated the upload.php described in the tutorial into something like this:
def upload(self):
[...]
When the server receives the request I can see that cherrypy.request.headers['Content-Length'] == 5676
which is the length of the file I'm trying to upload, so I assume the whole file has been sent to the server.
How do I get the content of the file?
At its minimum it looks like the following. Tested in Firefox and Chromium. If you need to support legacy browsers I'd look at some JavaScript library for polyfills and fallback.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import shutil
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8,
}
}
class App:
#cherrypy.expose
def index(self):
return '''<!DOCTYPE html>
<html>
<head>
<title>CherryPy Async Upload</title>
</head>
<body>
<form id='upload' action=''>
<label for='fileselect'>Files to upload:</label>
<input type='file' id='fileselect' multiple='multiple' />
</form>
<script type='text/javascript'>
function upload(file)
{
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function(event)
{
console.log('progess', file.name, event.loaded, event.total);
});
xhr.addEventListener('readystatechange', function(event)
{
console.log(
'ready state',
file.name,
xhr.readyState,
xhr.readyState == 4 && xhr.status
);
});
xhr.open('POST', '/upload', true);
xhr.setRequestHeader('X-Filename', file.name);
console.log('sending', file.name, file);
xhr.send(file);
}
var select = document.getElementById('fileselect');
var form = document.getElementById('upload')
select.addEventListener('change', function(event)
{
for(var i = 0; i < event.target.files.length; i += 1)
{
upload(event.target.files[i]);
}
form.reset();
});
</script>
</body>
</html>
'''
#cherrypy.expose
def upload(self):
'''Handle non-multipart upload'''
filename = os.path.basename(cherrypy.request.headers['x-filename'])
destination = os.path.join('/home/user', filename)
with open(destination, 'wb') as f:
shutil.copyfileobj(cherrypy.request.body, f)
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
I have been working on a game server written in python3. My goal is to keep the communication options on it very open so that multiple different clients can easily connect. Currently all communication has been through telnet using miniboa. I would like to have the option to allow for web based clients as well. It seems like the easiest option for that would be to allow websocket connections. I have been playing around with websockify which works, however I would prefer to not use a proxy if possible because then all the connections appear to come from the proxy. Ideally what I would like is something that I can put into my telnet server to recognize a websocket handshake request (as compared to the regular requests), return the proper handshake, then keep the connection going so the commands being sent/recieved through telnet and websockets are the same. I haven't been able to find anything that allows me to do this automatically so I have been experimenting around with writing my own code to recognize a websocket handshake and reply with a corresponding handshake. I have looked at many other posts and examples, epecially python websocket handshake (RFC 6455) which I modified and converted to the following test program
#!/usr/bin/env python3
from miniboa import TelnetServer
from base64 import b64encode
from hashlib import sha1
clientlist = []
def client_connects(client):
clientlist.append(client)
def client_disconnects(client):
clientlist.remove(client)
def process_clients():
for client in clientlist:
if client.active and client.cmd_ready:
total_cmd = client.get_command()
print("incoming = {}" .format(total_cmd))
if total_cmd.find(" ") != -1: # breaking apart incoming command
cmd, cmd_var = total_cmd.split(" ", 1)
else:
cmd = total_cmd
cmd_var = ""
if cmd == "Sec-WebSocket-Key:":
GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
cmd_var = cmd_var + GUID
encoded = cmd_var.encode('utf-8')
response_key = b64encode(sha1(encoded).digest())
websocket_answer = (
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: {key}\r\n\r\n',
)
handshake = '\r\n'.join(websocket_answer).format(key=response_key)
client.handshakestr = handshake
if cmd == "Upgrade:":
print("Sending handshake:")
print(client.handshakestr)
print("End of Handshake")
client.send(client.handshakestr)
server = TelnetServer(port=6112, on_connect=client_connects, on_disconnect=client_disconnects)
while True:
process_clients()
server.poll()
which seems to get me past the initial handshake but then it immediately drops. From the output it looks like the browser is expecting something further but I can't figure out what. For a client I am using the following code on firefox 29.0 I downloaded from http://opiate.github.io/SimpleWebSocketServer/
<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
function init()
{
document.myform.url.value = "ws://localhost:8000/"
document.myform.inputtext.value = "Hello World!"
document.myform.disconnectButton.disabled = true;
}
function doConnect()
{
websocket = new WebSocket(document.myform.url.value);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
function onOpen(evt)
{
writeToScreen("connected\n");
document.myform.connectButton.disabled = true;
document.myform.disconnectButton.disabled = false;
}
function onClose(evt)
{
writeToScreen("disconnected\n");
document.myform.connectButton.disabled = false;
document.myform.disconnectButton.disabled = true;
}
function onMessage(evt)
{
writeToScreen("response: " + evt.data + '\n');
}
function onError(evt)
{
writeToScreen('error: ' + evt.data + '\n');
websocket.close();
document.myform.connectButton.disabled = false;
document.myform.disconnectButton.disabled = true;
}
function doSend(message)
{
writeToScreen("sent: " + message + '\n');
websocket.send(message);
}
function writeToScreen(message)
{
document.myform.outputtext.value += message
document.myform.outputtext.scrollTop = document.myform.outputtext.scrollHeight;
}
window.addEventListener("load", init, false);
function sendText() {
doSend( document.myform.inputtext.value );
}
function clearText() {
document.myform.outputtext.value = "";
}
function doDisconnect() {
websocket.close();
}
</script>
<div id="output"></div>
<form name="myform">
<p>
<textarea name="outputtext" rows="20" cols="50"></textarea>
</p>
<p>
<textarea name="inputtext" cols="50"></textarea>
</p>
<p>
<textarea name="url" cols="50"></textarea>
</p>
<p>
<input type="button" name=sendButton value="Send" onClick="sendText();">
<input type="button" name=clearButton value="Clear" onClick="clearText();">
<input type="button" name=disconnectButton value="Disconnect" onClick="doDisconnect();">
<input type="button" name=connectButton value="Connect" onClick="doConnect();">
</p>
</form>
</html>
So does anyone know either:
1. an easier way to use websockets with my telnet server?
2. what is wrong with my attempts at responding to a websocket connection?
3. if I should give up and settle for using a proxy with web connections?
Tornado has a nice compliant WebSocket handler. You can use that, or use it for reference when building yours.