I'm working on a web interface for a project and need to plot a realtime chart with some data I'll get from an API I'm building. The API runs on http://SERVER_IP:5000/signal and my web interface on http://SERVER_IP:80/, both of them with Flask.
I was having CORS issues at the very beginning and then found out about Flask-CORS. I tried to implement that and now I'm getting no errors on the browser console, but no messages either! I've added some console messages for debugging, but it doesn't seem alright.
On my dashboard, I try to reach the API with the following code:
const source = new EventSource("http://SERVER_IP:5000/signal", {withCredentials: true});
console.log ("Things started!!!");
source.onmessage = function (event) {
console.log ("An event's just happened");
// parse data and do stuff
}
and in my API, I set Flask-CORS like this:
self.__cors = CORS(self.__app, supports_credentials = True)
and the route like this:
#self.__app.route('/signal')
def get_signal():
import json
def get_data():
while True:
json_data = json.dumps(...)
yield "{}\n\n".format(json_data)
time.sleep(1)
return Response(get_data(), mimetype='text/event-stream')
Then, when I open my web browser and open console, I can see the "Things started!!!" message, but no "An event's just happened" and no data on the chart.
I was malforming my response. As I found out here, my "get_data" method should yield "data: ...", so after I added the "data: " after my data, things started to work pretty well.
Related
I a newbie in backend and I am trying to connect the React with python, so basically it should work as it follows:
React sends inputted data in firebase -> python extracts it and works with it and sends it back in firebase -> react extracts it and works with it.
At this moment I've done all the necessary code however I don't know how to connect them.
Here is the React snippet were it sends the message to firebase:
console.log(message)
const Push = () => {
const db = getDatabase();
set(ref(db, 'users/'), {
message : message
});
}
Here is the pythonfile:
firebase = Firebase(config) #extracting info
db = firebase.database()
users = db.child("users").get()
message = users.val()
query = ''
for element in message:
query = message[element]
loc = Nominatim(user_agent="GetLoc")#working with it
getLoc = loc.geocode(f"{query}")
extract = db.child("extract").push(f"{getLoc.latitude},{getLoc.longitude}")#sending it in firebase
So basically the question is how do I make them work together, and how can I do the operations that I said about above?
I've heard about Flask, but I don't know how it actually works...
I'm trying to wrap my head around this socketio thing and have hit a wall regarding sending a message from my Flask backend to my JavaScript frontend. My app is a consolidated control panel for my church's PTZ camera and OBS Studio. Here are the logical steps I'm trying to implement:
Connect Flask to OBS via the OBS websocket.
Receive notification in Flask when the Graphics source visibility in OBS changes.
Send a message from Flask to my JS frontend to let it know that the Graphics source has changed visibility (making a two-way sync with the button on my control panel and the actual source in OBS).
Steps 1 and 2 are working, but I can't get the 3rd one to work.
Here is my Python where I use the message from OBS to call the function to sync with the JS frontend. (I know the sync_graphics_toggle runs, but the JS never gets the emit message.)
def on_visibility_change(message):
if (message.getItemName() == "Graphics"):
sync_graphics_toggle(message.getItemVisible())
def sync_graphics_toggle(obs_graphics_visible):
socketio.emit('syncGraphicsToggle', obs_graphics_visible, namespace='/main')
In the JavaScript (with just a console log to test):
$(document).ready(function() {
const socketMain = io("/main");
socketMain.on("connect", function() {
console.log("Made connection in graphics-preview", socketMain.id);
});
socketMain.on("syncGraphicsToggle", function(obsGraphicsVisible) {
console.log("Got syncGraphicsToggle command")
});
}
I have verified that I'm connected, but I don't get the message from Flask.
I have other implementations of socketio in my code that work fine, but those are initiated by actions on the webpage. So, I'm thinking there is something I don't understand about sending messages that are initiated by the server.
Any pointers would be greatly appreciated.
John
Windows 10
Python = 3.10.4
Flask = 2.2.2
Python Socket.IO = 5.7.2
JS Socket.IO = 4.5.3
I have been using Google Cloud Functions for over a week now and they have been great. I used a simple python 3.9 function to print a string to my terminal in my Next.js app (for testing purposes) and it was working great. Here is my sample Google Cloud Function.
def hello_world(request):
"""Responds to any HTTP request.
Args:
request (flask.Request): HTTP request object.
Returns:
The response text or any set of values that can be turned into a
Response object using
`make_response <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>`.
"""
request_json = request.get_json()
if request.args and 'message' in request.args:
return request.args.get('message')
elif request_json and 'message' in request_json:
return request_json['message']
else:
return f'Function ran'
And here is my Next.js code that calls the function:
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next"
import { GoogleAuth } from "google-auth-library"
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const url = process.env.FUNCTION_URL as string
//Example with the key file, not recommended on GCP environment.
const auth = new GoogleAuth({ keyFilename: process.env.KEYSTORE_PATH })
//Create your client with an Identity token.
const client = await auth.getIdTokenClient(url)
const result = await client.request({ url })
console.log(result.data)
res.json({ data: result.data })
}
I wrote another function to do the same thing and now every function just prints out raw html to the console? When I open the text in an index.html file it looks like this.
I rewrote the original cloud function exactly and even that doesn't work anymore. It prints that same html to the console. What is going on? My code is exactly the same and it breaks now...?
It was a simple "needle in the haystack" type of fix. I was getting the function url from within the cloud function listing view, clicking the Actions Menu dropdown on the desired function, and selecting "Copy Function." This took me to the configuration display page where it had the function url in the "Trigger" section.
Notice what I circled in red. For some reason this page appends a -1 to the function url. This is what made it fail for me. In my code I deleted the -1 from the url stored in the process.env.FUNCTION_URL variable. That made it work!
I have a web app utilizing Python Flask where I am trying to use Server Sent Events (SSEs) to push messages to web pages without having to poll from the client side or request that data. I'm using Redis to listen for new data which will then be sent to the web page. To start and make sure that I can use SSEs correctly, I've used a template similar to an example like this (How to implement server push in Flask framework?).
The problem I'm running into is that every time the client receives a message, the EventSource onmessage() method is called and delivers the message properly, but then the .onerror() method immediately gets triggered, causing the client to try to reconnect. This results in the '/listen' endpoint being called over and over and over, leading to the creation of many redis pubsub objects that are redundant and subscribe to the same channels.
The python code that runs the flask app is as follows
import flask
from flask_bootstrap import Bootstrap
from redis import Redis
from flask_wtf import FlaskForm
app = flask.Flask(__name__)
bootstrap = Bootstrap(app)
red = Redis(host='localhost', port=6379, db=0)
#app.route('/listen')
def listen():
pubsub = red.pubsub()
pubsub.subscribe('chat')
def stream():
for message in pubsub.listen():
if message['type'] == 'message':
msg = f"data: {message['data'].decode('utf-8')}\n\n"
yield msg
for msg in stream():
return Response(msg, mimetype='text/event-stream')
#app.route('/sse_page', methods=['GET', 'POST'])
def sse_page():
form = FlaskForm()
return render_template('sse_page.html', title='Server Push Testing', form=form)
if __name__ == "__main__":
app.run(port=8000, threaded=True, debug=True)
The corresponding section of sse_page.html where I try to open the EventSource and listen for the events stream is
<body>
<div id="target_div">Watch this space...</div>
</body>
<script>
var source = new EventSource("/listen");
source.onmessage = function (event) {
console.log('data: ', event)
$("#target_div").text(event.data)
};
source.onerror = function (event) {
console.log('error ', event)
};
source.onopen = function (event) {
console.log('open', event)
};
</script>
Using the redis-cli to send messages like those seen
here (and transcribed below)
127.0.0.1:6379> publish chat a
(integer) 1
127.0.0.1:6379> publish chat b
(integer) 2
Result in the console logging messages from Eventsource.onopen(), Eventsource.onmessage(), and Eventsource.onerror() for every single message, as seen
here.
I cannot figure out why the eventsource has an error after every single message that is received or how to prevent that from happening.
The answer to this question is NOT a problem in the code itself.
What this ended up being was an issue with the anti-virus security that was being used on the machine. Using Sophos AV Endpoint was causing each SSE to be treated as a download, and so any of the text data was unable to be streamed until the 'download was complete'.
This is (apparently) a known issue (see link https://community.sophos.com/on-premise-endpoint/f/sophos-endpoint-software/74878/server-sent-events-blocked-by-download-scanner) and there are a couple of ways to deal with it. You can either disable web scanning by Sophos (which does not work if you do not have administrator permissions) or run the flask app securely over HTTPS (https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https has a great tutorial).
Credit too should go to this post (JavaScript EventSource SSE not firing in browser), which is how I was able to find that the AV software was what ended up causing my issues.
Apologies because the only web development I know is of the django/python kind and am probably guilty of mixing my code idioms ( REST vs django URL dispatch workflow)
I have a URL handler which serves as a callbackUrl to a subscription for my Glassware. I am getting a POST to the handler , but the request object seems empty.
I am sure I am understanding this wrong but can someone point me in the direction of getting the "REPLY" information from a POST notification to a callbackURL.
My URL Handler is
class A600Handler(webapp2.RequestHandler):
def post(self):
"""Process the value of A600 received and return a plot"""
# I am seeing this in my logs proving that I am getting a POST when glass replies
logging.info("Received POST to logA600")
# This is returning None
my_collection = self.request.get("collection")
logging.info(my_collection)
# I also tried this but self.sequest.POST is empty '[]' and of type UnicodeMultiDict
# json_request_data = json.loads(self.request.POST)
#util.auth_required
def get(self):
"""Process the value of A600 received and return a plot"""
logging.info("Received GET to this logA600")
I have the following URL Handler defined and can verify that the post function is getting a "ping" when the user hits reply by looking at the app-engine logs.
MAIN_ROUTES = [
('/', MainHandler),('/logA600',A600Handler),
]
How do I extract the payload in the form of the voice transcribed text sent by the user?. I am not understanding The "parse_notification" example given in the docs
Did you try request.body? The docs for request.POST state
"If you need to access raw or non-form data posted in the request, access this through the HttpRequest.body attribute instead."
If the API isn't using form data in its post, you'll likely find the contents in request.body. The docs to which you linked indicate that the content will be placed as JSON in the body instead of form data ("containing a JSON request body"). I would try json.loads(request.body).
I am also having this issue of Mirror API calling my application for notifications, and those notifications are empty. My app runs on tomcat so its a java stack. All the samples process the notification like this:
BufferedReader notificationReader = new BufferedReader(
new InputStreamReader(request.getInputStream()));
String notificationString = "";
// Count the lines as a very basic way to prevent Denial of Service
// attacks
int lines = 0;
while (notificationReader.ready()) {
notificationString += notificationReader.readLine();
lines++;
// No notification would ever be this long. Something is very wrong.
if (lines > 1000) {
throw new IOException(
"Attempted to parse notification payload that was unexpectedly long.");
}
}
log.info("got raw notification " + notificationString);
For me this is always logging as empty. Since a notification url must be https, and for testing I could not use an IP address, I have setup dyndns service to point to my localhost:8080 running service. This all seems to work but I suspect how dyndns works is some type of forward or redirect here post data is removed.
How can I work around this for local development?
Updated:
Solved for me.
I found closing the response before reading request caused issue that request.inputStream was already closed. MOving this
response.setContentType("text/html");
Writer writer = response.getWriter();
writer.append("OK");
writer.close();
To after I fully read in request notification into a String solved the issue.