Summary: I am trying to teach myself Flask and React with the tools I have at hand during the pandemic. I am hampered by the lack of a good React editor for remote files, I suspect I may not be seeing error messages such an editor would be showing me, and I suspect that the python script I am trying to invoke is never getting touched.
Any help, including how to better see error messages, or what might be mis-configured, are desperately appreciated.
What I have:
A command line only interface to an Ubuntu 18.04 container. This means: no remote desktop, no GUI, no specialized react editor. I cannot do anything about this.
React server up and running on container port 3000, accessible to the outside world with a path prefix per this question and answer. Meaning, I can run react from the text terminal command line, edit react project files in another window with vi, point a browser to it from well outside the system and it works.
Flask server up and running on container port 3001 (port 5000 already doing something else) and React server configured with port 3001 as a proxy. God help me, I am using the DANGEROUSLY_DISABLE_HOST_CHECK option to get that to work. No special consideration for port prefixing or port forwarding-- this seems all internal to the container, so I don't think any special handling is necessary.
A tutorial project as per this site. I will include the two relevant code files below.
The results of this are:
No errors in either of the terminals where I invoked the react and flask servers.
The web page displays, but improperly: Rather than displaying the correct time, the page displays only whatever I pre-loaded into currentTime.
I am highly skeptical that the python script is ever running at all: If I mangle the api.py file with egregious syntactical errors... no change in behavior. If I mangle the App.js with errors, the system dies. If I mangle the App.js with the specific error of fetch('/garbage').then... rather than fetch('/time').then... there are still no errors, which I find HIGHLY suspicious.
This makes me think I may not be seeing some critical error or warning messages.
My suspicion was that this might be some type of CORS issue, but two attempts at adapting solutions to this question failed.
Code from App.js:
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
const [currentTime, setCurrentTime] = useState(2);
useEffect(() => {
fetch('/time').then(res => res.json()).then(data => {
setCurrentTime(data.time);
});
}, []);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<p>The current time is {currentTime}.</p>
</header>
</div>
);
}
export default App;
Code from api.py (not including CORS attempts):
import time
from flask import Flask
app = Flask(__name__)
#app.route('/time')
def get_current_time():
return {'time': time.time()}
UPDATE:
On the advice of this answer, I added some console logging statements and looked at the console logging in Chrome.
App.js:11 GET http://<CORRECT_ADDRESS>/time 404 (Not Found)
(anonymous) # App.js:11
commitHookEffectListMount # react-dom.development.js:19731
commitPassiveHookEffects # react-dom.development.js:19769
callCallback # react-dom.development.js:188
invokeGuardedCallbackDev # react-dom.development.js:237
invokeGuardedCallback # react-dom.development.js:292
flushPassiveEffectsImpl # react-dom.development.js:22853
unstable_runWithPriority # scheduler.development.js:653
runWithPriority$1 # react-dom.development.js:11039
flushPassiveEffects # react-dom.development.js:22820
(anonymous) # react-dom.development.js:22699
workLoop # scheduler.development.js:597
flushWork # scheduler.development.js:552
performWorkUntilDeadline # scheduler.development.js:164
Note 1: Port 3001 is not included, which seems wrong.
Note 2: The path prefix is not included, which seems correct.
I would have expected this to be:
App.js:11 GET http://<CORRECT_ADDRESS>:3001/time
If the port, and not the path prefix, needs to be included in that call, how do I arrange that?
Flask is running on port 3001, and the last line of package.json is:
"proxy": "http://localhost:3001"
Further Update:
using fetch('http://<CORRECT_ADDRESS>:3001/time') now yields the following console errors:
WebSocket connection to 'ws://<CORRECT_ADDRESS>/sockjs-node' failed: Error during WebSocket handshake: Unexpected response code: 404
The development server has disconnected.
Refresh the page if necessary.
GET http://<CORRECT_ADDRESS>:3001/time net::ERR_CONNECTION_TIMED_OUT
Uncaught (in promise) TypeError: Failed to fetch
So the GET seems to be well-formed, now, but times out due to the failed WebSockets call, which may or may not be correctly formed.
You may be seeing an infinite loop, where state is not having a chance to update.
Try this:
#app.route('/time')
def get_current_time():
print( time.time() )
return {'time': time.time()}
Is /time getting hits? Because your useEffect() code will run every time the state changes, which is every time it runs: infinite loop.
If /time is not getting hits, then your problem is in that relative path. Try this:
useEffect(() => {
console.log('fetching');
fetch('/time').then(res => res.json()).then(data => {
console.log('fetched:' + data);
setCurrentTime(data.time);
});
}, []);
Next, check your ports. It sounds like you may have React/Webpack running on 3001, and the server running on 5000.
Try these:
http://localhost:5000/time
http://localhost:3000/time
http://localhost:3001/time
Related
When using Angular 5 with Flask and ng build --aot --watch, I am having major issues getting Angular's routing and refreshing the page to work.
I have come across many attempts to fix this, but none of them when applied to my project work (and the example projects provided when I try it there also do not seem to work).
What happens (depending on the thing I try) are these:
It can't find the path at all and I get a Jinja2 error
It can't find the document for localhost:5015/ (the port I'm using) and I get a 404 and a white screen
It loads index.html, but upon refresh it gets a 404 (implying that the browser is attempting to fetch the page from the server, not using the Angular routing system)
I get the same issue as this one: Syntax Error in Angular App: Unexpected token <
I am under the impression this is a normal issue with all SPAs and that the solution is to always redirect the server to index.html. We have done this (AFAIK), and attempted multiple ways to do this:
Our structure is roughly the following:
- ProjectDir
- reverseProxy_Calls
- scripts
- swagger_yaml_definitions
- templates
- e2e
- dist
- src
- app
And our server.py contains this (among other stuff)
Swagger(app)
app.template_folder = "./templates/dist/"
app.static_folder = "./templates/dist/"
app.static_path = "./templates/dist/"
app.static_url_path = "./templates/dist/"
app.instance_path = "./templates/dist/"
config = settings.config()
config.read("settings.cfg")
#app.route('/', defaults={'path': ''}, methods=['GET', 'POST'])
#app.route('/<path:path>', methods=['GET', 'POST'])
def get_resource(path): # pragma: no cover
# If we are devmode, proxy through flask.
if config.getKey("devmode") == True:
url = "http://localhost:%s/%s" % (config.getKey('ng_port'), path)
j = requests.get(url, stream=True, params=request.args)
return make_response(j.text)
# return the results
# return render_template('dist/index.html')
# return app.send_static_file("index.html")
return send_from_directory('dist/', path)
if __name__ == '__main__':
app.run(debug=True, port=int(config.getKey("port"))) # , host='0.0.0.0')
^ A variety of ways here show various attempts to handle this.
I have also looked at and tried projects that use variations of the following:
Flask Cli
Flask-Script
Flask-Bootstrap
None of them have solved this problem when I have tried them.
I have been told that using the hashRoute location strategy in Angular rather than the History PushState strategy will also work, however when I attempted to do that it had the same behavior of losing the page upon refresh (additionally we wish to use server side rendering in the future, and hashRoute location strategy prevents this possibility, among many other downsides).
Possibly related is that Flask seems to reload or re-render the page when it does find the index.html and routes initially, wiping anything printed to the browser dev console. I have seen this in some of the flask examples, but not all, and I'm uncertain what causes this.
When running via ng serve --aot --prod, things work exactly correctly and as expected. It's only when serving via Flask that things break down.
Apologies for the long question and thank you for your time in advance.
I am under the impression this is a normal issue with all SPAs and
that the solution is to always redirect the server to index.html. We
have done this (AFAIK), and attempted multiple ways to do this
You also need to redirect all HTTP errors to index.html.
Been working with this for a while and found a few helpful things, but I'm not good with AJAX yet...
I'm working to make a chromium-browser kiosk on the Raspberry Pi (which has been fine) but when in kiosk mode, there's not a "shutdown" button. I'm displaying a local HTML file in the chromium-browser and I want to create a button in the local HTML file that will shutdown the computer using AJAX/JQuery by calling a simple python code I made:
#! /usr/bin/python -u
import os
import shutil
import sys
os.system('sudo shutdown -h now')
I found this:
$.ajax({
type: "POST",
url: "~/shutdown.py",
data: { param: text}
}).done(function( o ) {
// do something
});
How do I connect this though? there's no output from my python, just want to call the python code and have the raspberry pi shutdown.
The python code when I run it in its own terminal shuts down the computer as expected.
Or if you have any other ideas for shutting down the Rpi while in Kiosk mode in the most "user friendly" way, let me know! The people using this won't know about using the terminal or SSH-ing in...
Thanks!
Probably the easiest way would be to run a local web server that listens for a request and then shuts down the computer. Instead of just displaying a local HTML file, actually serve it from flask. This gives you much more freedom, since you can run the commands server-side (even though the server and client are the same in this case) where you don't have restrictions like you do within a browser environment.
You could do this with flask.
Create a directory like this
/kiosk
/templates
index.html
app.py
The index.html is your current html page. Here is what app.py would look like.
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def index():
return render_template('index.html')
#app.route("/shutdown")
def shutdown():
os.system('sudo shutdown -h now')
if __name__ == "__main__":
app.run()
Your ajax call would look like this
$.ajax({
type: "POST",
url: "/shutdown"
});
Then just cd into the app directory and run python app.py, which starts the web application. Then open a browser and go to localhost:5000
I am using gunicorn and flask for a web service. I am trying to get my head around running a streaming route (not sure if that is the correct terminology).
my route looks like this:
#app.route('/delay')
def delay():
from time import sleep
def delay_inner():
for i in range(10):
sleep(5)
yield json.dumps({'delay': i})
return Response(delay_inner(), mimetype="text/event-stream")
I expect that the server would yield the output each time that delay_inner does a yield. But, what I am getting is all the json responses at once, and only when the delay_inner finishes execution.
What am I missing here?
--EDIT--
I have fixed the issue for Flask and Gunicorn, I am able to run it as expected by using the flask server, and by going to the Gunicorn port. It streams the data as expected. However, and I should have mentioned this in the original post, I am also running behind nginx. And that is not set up correctly to stream. Can anyone help with that?
You need to turn off the nginx proxy buffering.
location /delay {
proxy_pass http://127.0.0.1:8080;
proxy_buffering off;
}
and reload the config
nginx -s reload
I'm trying to use Flask-Sockets with the example code:
sockets = Sockets(app)
#sockets.route('/echo')
def echo_socket(ws):
while True:
message = ws.receive()
ws.send(message)
Unfortunately, when simply going to the url /echo using my browser it gives me an error saying:
File "/Library/Python/2.7/site-packages/Flask-0.10-py2.7.egg/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/Library/Python/2.7/site-packages/flask_sockets.py", line 37, in __call__
environment = environ['wsgi.websocket']
KeyError: 'wsgi.websocket'
Anybody any ideas what I'm doing wrong? All tips are welcome!
[EDIT]
#jbub - Thanks for the tips! So to start off I now use gunicorn instead of the built-in dev-server. So I started it using gunicorn -k flask_sockets.worker -b 0.0.0.0:5000 main:app. I then inserted the code below in my views.py i nwhich the echo_test.html is the code you supplied. When I now visit /echo_test, I indeed get a prompt saying "socket closed".
sockets = Sockets(app)
#sockets.route('/echo')
def echo_socket(ws):
while True:
message = ws.receive()
ws.send(message)
#app.route('/echo_test', methods=['GET'])
def echo_test():
return render_template('echo_test.html')
But let's say my aim is to have a word (randomly chosen from a list) on a page which gets updated with other values randomly chosen from the list. Would you have any tips on achieving that?
Ah, thats the problem, you cant just visit the websocket endpoint with regular GET request, that way the wsgi.websocket will not be set to environ.
Also use gunicorn not the dev server, it comes with preconfigured worker:
# install from pip
pip install gunicorn
# run app located in test.py module (in test.py directory)
gunicorn -k flask_sockets.worker test:app
I made quick example here, be sure to update the address and port to match your setup.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var ws = new WebSocket("ws://localhost:8000/echo");
ws.onopen = function() {
ws.send("socket open");
};
ws.onclose = function(evt) {
alert("socket closed");
};
</script>
</head>
</html>
That way the browser sends a request to the server, indicating that it wants to switch protocols from HTTP to WebSocket.
Feel free to read some more about websockets here:
https://www.rfc-editor.org/rfc/rfc6455
http://www.websocket.org/aboutwebsocket.html
It seems that Flask-Sockets does not provided a socket server so you either have to set up nginx to proxy web sockets, run your app using gunicorn or create a socket server yourself.
I found this helpful https://gist.github.com/lrvick/1185629
If using AWS, I found that sometimes need to edit the security group so that port 80 (and 443) are of type 'Custom TCP Rule' and not HTTP (and HTTPS)
Question: What would be a comparable solution to the example at this link, except implemented using gevent-socketio and Socket.io.js with bottle? I'm looking for the minimal solution that will simply pass some traffic in a loop from the client to the server and back to the client using gevent-socketio, Socket.io.js, and bottle.
Background: I have developed a simple web-app that provides a web-based terminal for a remote custom shell (cli) on the server. The browser (client) collects shell commands from a form input field, passes the command over a web-socket to a gevent.pywsgi.WSGIServer handling the requests via the geventwebsocket.WebSocketHandler handler, which supplies the command to the shell, while asynchronously returning output via the socket to a textarea field in a form in the client's browser. This is based on a great, little example provided by the bottle team:
http://bottlepy.org/docs/dev/async.html#finally-websockets
Provided here for redundancy:
example_server.py:
from bottle import request, Bottle, abort
app = Bottle()
#app.route('/websocket')
def handle_websocket():
wsock = request.environ.get('wsgi.websocket')
if not wsock:
abort(400, 'Expected WebSocket request.')
while True:
try:
message = wsock.receive()
wsock.send("Your message was: %r" % message)
except WebSocketError:
break
from gevent.pywsgi import WSGIServer
from geventwebsocket import WebSocketHandler, WebSocketError
server = WSGIServer(("0.0.0.0", 8080), app,
handler_class=WebSocketHandler)
server.serve_forever()
client.html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var ws = new WebSocket("ws://example.com:8080/websocket");
ws.onopen = function() {
ws.send("Hello, world");
};
ws.onmessage = function (evt) {
alert(evt.data);
};
</script>
</head>
</html>
Motivation: My existing app works great in the latest version of Firefox and Chrome. IE support is non-existent, and Safari compatibility is middlin'. I'm ultimately looking for a cross-browswer solution to communicate shell commands and output between the client and server. If I had a simple example for bottle, I think I could move forward more quickly.
Incidentally, I looked at the gevent-socketio examples and even a bottle example, but all of these examples are too different from the above simple example for me to make the leap in application. (The gevent-socketio examples look nothing like the bottle apps, which which I'm familiar. And, the bottle example doesn't actually show how to communicate with the client.)
Thanks! :)
Circus! the process runner and watcher built on top of zmq, use bottle and socketio for the web interfaces:
https://github.com/mozilla-services/circus/blob/master/circus/web/circushttpd.py
https://github.com/mozilla-services/circus/blob/master/circus/web/server.py
The source code is simple enough for helping you to get started to build a bigger app with bottle and socketio.
Otherwise, I advice you to move to sockjs! which a more generic implementation with better support for different backends.
This other thread can help you :
SockJS or Socket.IO? Worth to recode ajax-based page?