Socketio implementation with FastApi and React - python

I have the following techstack
FastApi - backend
React - frontend
and want to implement socketio(not Websockets provided by FastApi). It lacks documentation in both FastApi and Socketio

As needed we are using python-socketio for backend socket server and on react we will be using socket.io-client.
After installation we need to setup a socket server.
Backend Implementation
# socket.py
def handle_connect(sid, environ):
logger.info(f"Socket connected with sid {sid}")
class SocketManager:
def __init__(self, origins: List[str]):
self.server = socketio.AsyncServer(
cors_allowed_origins=origins,
async_mode="asgi",
logger=True,
engineio_logger=True,
)
self.app = socketio.ASGIApp(self.server)
#property
def on(self):
return self.server.on
#property
def send(self):
return self.server.send
def mount_to(self, path: str, app: ASGIApp):
app.mount(path, self.app)
socket_manager = SocketManager(settings.origins)
socket_manager.on("connect", handler=handle_connect)
Double check your cors origins. Also you can add other handlers as well using socket_manager.on.
#main.py
from socket import
app = FastAPI(...)
socket_manager.mount_to("/ws", app)
Frontend Implementation
Basic code for integration is as simple as
import { io } from "socket.io-client";
const socket = io("ws://localhost:8000", {{ path: "/ws/socket.io/", transports: ['websocket', 'polling'] }});
socket.on("connect", () => { console.log("Connected", socket.id) });
socket.on("response", () => { console.log("Response", socket.id) });
socket.on("message", data => { console.log(data) });
For my project i created a context for this as follows
import React, { createContext, useContext, useEffect, useState } from 'react';
import { io } from "socket.io-client";
import { useToastContext } from './ToastContext';
export const SocketContext = createContext()
export const SocketProvider = ({ children, uri, options }) => {
const [socketIo, setSocketIo] = useState()
const { sendToast } = useToastContext();
useEffect(() => {
const socket = io(uri, options);
socket.on("connect", () => { console.log("Connected", socket.id) });
socket.on("response", () => { console.log("Response", socket.id) });
socket.on("message", data => {
sendToast(data)
});
setSocketIo(socket)
}, [])
return (
<SocketContext.Provider value={socketIo}>
{children}
</SocketContext.Provider>
)
}
export const useSocket = () => {
return useContext(SocketContext)
}
Now to finally send a message from your server to client you can do
socket_manager.send("Hello World")
Points worth noting
CORS origin should be exactly same if it is http://localhost:3000 from frontend then it should be http://localhost:3000 and not http://localhost:3000/. Look for the backslash
Also the socketio documentation says transports: ['websocket', 'polling'] is default but when i remove this. It gives me cors error. Documentation might be out of date.

Related

How to fetch data analyzed in python to node.js and pass it to angular?

I am new to angular and i want to display JSON data from python to angular with the help of node.js and I used child process to connect python and node.js but I dont know how to pass it to angular service
node.js file
const express = require('express')
const { spawn } = require('child_process')
const app = express()
const port = 8000
app.get('/', (req, res) => {
let dataToSend
let largeDataSet = []
// spawn new child process to call the python script
const python = spawn('python', ['test.py'])
// collect data from script
python.stdout.on('data', function (data) {
console.log('Pipe data from python script ...')
//dataToSend = data;
largeDataSet.push(data)
})
// in close event we are sure that stream is from child process is closed
python.on('close', (code) => {
console.log(`child process close all stdio with code ${code}`)
// send data to browser
res.send(largeDataSet.join(''))
})
})
app.listen(port, () => {
console.log(`App listening on port ${port}!`)
})
Technically you just have to send a Http GET request from your service.
I suggest that you should read and follow this offical http client guide to set it up correctly.
Here is a simple service snippet. This should be enough.
#Injectable({
providedIn: 'root',
})
export class MyService {
constructor(private http: HttpClient) {}
getData(): Observable<any> {
const url = '';
return this.http.get(url);
}
}

Server Error in Android Volley Request Status Code 500

I have deployed an Flask API in Heroku. Trying to hit the Flask API through Android Volley POST request. But the log file shows NetworkUtility.shouldRetryException: Unexpected response code 500.
I have Tested on Postman.it works perfect.I don't know what is the problem, stuck here for 2 days.
My URL for the Heroku app is correct.
In my Heroku app log file : also shows an error "TypeError: expected string or bytes-like object".
Here is my MainActivity.java :
public class MainActivity extends AppCompatActivity {
EditText question;
Button btn;
TextView textView;
Button reset;
String url = "https://krishokbot.herokuapp.com/chatbot";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
question = (EditText) findViewById(R.id.question);
btn = (Button) findViewById(R.id.btn);
textView = (TextView) findViewById(R.id.textView);
reset = (Button) findViewById(R.id.reset);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
StringRequest stringRequest = new StringRequest(Request.Method.POST,url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
String data = jsonObject.getString("response");
textView.setText(data);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
if (networkResponse != null) {
Log.e("Volley", "Error. HTTP Status Code:" + networkResponse.statusCode);
}
if (error instanceof TimeoutError) {
Log.e("Volley", "TimeoutError");
} else if (error instanceof NoConnectionError) {
Log.e("Volley", "NoConnectionError");
} else if (error instanceof AuthFailureError) {
Log.e("Volley", "AuthFailureError");
} else if (error instanceof ServerError) {
Log.e("Volley", "ServerError");
} else if (error instanceof NetworkError) {
Log.e("Volley", "NetworkError");
} else if (error instanceof ParseError) {
Log.e("Volley", "ParseError");
}
Log.d("Maps:", " Error: " + error.getMessage());
}
}) {
#Override
protected Map<String,String> getParams() {
Map<String,String> params = new HashMap<String, String>();
params.put("question", question.getText().toString());
return params;
}
};
RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
queue.add(stringRequest);
}
});
}
}
from flask import Flask, render_template, jsonify, request
import chatbot_response
app = Flask(__name__)
#app.route('/')
def index():
return "hello_world"
ar
#app.route('/chatbot', methods=['POST'])
def chatbotResponse():
question = request.args.get('question')
response = chatbot_response.chatbot_response(question)
return jsonify({"response": str(response)})
if __name__ == '__main__':
app.run()
Heroku log Image link : [https://ibb.co/jRtBfN4]

How to post request to Flask using vue?

I want to post data from Vue to flask
If I don't use parameter data: {},it's no problem.
If I use, error occurs
and this is the server (HTTP Status Code is 200)
Why is this happening? What should I do?
code:
Home.vue
<script>
import axios from 'axios'
export default {
created() {
this.getData();
},
methods: {
getData() {
axios({
baseURL: 'http://127.0.0.1:5001',
url: '/recData',
method: 'post',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
}
},
}
</script>
fls_2.py
from flask import Flask, request
from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r'/*': {'origins': '*'}})
#app.route('/')
def index():
return 'Hello World!'
#app.route('/recData',methods=['GET','POST'])
def recData():
if request.method=='POST':
return '--- post ---'
if request.method=='GET':
return '--- get ---'
if __name__=='__main__':
app.run(port=5001, debug=True)
You can set a proxy in vue.config.js
module.exports = {
devServer: {
proxy: 'http://127.0.0.1:5001',
}
}
there is a good article about it in this link: https://medium.com/js-dojo/how-to-deal-with-cors-error-on-vue-cli-3-d78c024ce8d3
Add this to vue.config.js and restart your server after the configuration change.
devServer: {
proxy: {
"/": {
target: "http://localhost:5001",
ws: false,
changeOrigin: true,
logLevel: 'debug'
}
}
Use this in your component.
let url = "/recData";
let payload = {
firstName: "Fred",
lastName: "Flintstone"
}
axios.post(url, payload)
.then(res=>{
console.log(res);
})
.catch(err=>{
console.log(err);
});
I hope it works and I was able to help you.

Real time notifications with Django and Socket.io

currently i'm implementing real time notifications for my Django project.
I'm following instructions from this tutorial. Problem is, i'm using Socket.io 1.4.5 and tutorial is written for pre-1.0 versions. So i had to adapt some code following 'Migrating from 0.9' guideline on Socket.io site. What i got is:
var http = require('http');
var server = http.createServer().listen(8002);
var io = require('socket.io')(server);
var cookie_reader = require('cookie');
var querystring = require('querystring');
var redis = require('redis');
// Supposedly this should store cookie set by Django
io.use(function(socket,accept){
var data = socket.request;
if(data.headers.cookie){
data.cookie = cookie_reader.parse(data.headers.cookie);
return accept(null, true);
}
return accept('error', false);
});
io.sockets.on('connection', function (socket) {
// Redis client
client = redis.createClient();
// Subscribe to notification channel
client.subscribe('notifications.' + socket.handshake.cookie['sessionid']);
console.log('subscribed');
//Grab message from Redis and send to client
client.on('message', function(channel, message){
console.log('on message', message);
socket.send(message);
});
// Unsubscribe
socket.on('disconnect', function() {
client.unsubscribe('notifications.' + socket.handshake.cookie['sessionid']);
});
});
When i'm running this script:
node notifications.js
After 2 seconds of silence i get this error:
client.subscribe('notifications.' + socket.handshake.cookie['sessionid']);
^
TypeError: Cannot read property 'sessionid' of undefined
at Namespace.<anonymous> (path/to/notifications.js)
at Namespace.emit (events.js:107:17)
at Namespace.emit (/path/to/node_modules/socket.io/lib/namespace.js:206:10)
at /path/to/node_modules/socket.io/lib/namespace.js:174:14
at process._tickCallback (node.js:355:11)
Can somebody point me to what i did wrong?
Just found what my mistake was.
To access cookie, instead of socket.handshake i should be using socket.request. So my current code looks like this now:
var http = require('http');
var server = http.createServer().listen(8002);
var io = require('socket.io')(server);
var cookie_reader = require('cookie');
var querystring = require('querystring');
var redis = require('redis');
io.use(function(socket,accept){
var data = socket.request;
if(data.headers.cookie){
data.cookie = cookie_reader.parse(data.headers.cookie);
return accept(null, true);
}
return accept('error', false);
});
io.on('connection', function (socket) {
// Redis client
client = redis.createClient();
// Subscribe to notification channel
client.subscribe('notifications.' + socket.request.cookie['sessionid']);
console.log('subscribed');
//Grab message from Redis and send to client
client.on('message', function(channel, message){
console.log('on message', message);
socket.send(message);
});
// Unsubscribe
socket.on('disconnect', function() {
client.unsubscribe('notifications.' + socket.request.cookie['sessionid']);
});
});

Emitting messages from python-shell to a browser in a Node Express App

I have an express app and I would like to be able to trigger python scripts via routes and emit the log into the browser.
I have created a route which triggers the python scripts correctly and outputs the log into the node console using python-shell module. How can I push this log to the browser in real time.
I have a button on an index.html page which triggers an Ajax post request to a /python route below. I have tried to implement this with socket.io but haven't managed to get my head around it. Is socket.io the way forward, please point me in the right direction or can someone recommend an alternative?
My file layout is:
server.js
var express = require('express');
var path = require('path');
var app = express();
var port = process.env.PORT || 3000;
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
/* A bit inbetween */
server.listen(port)
app/routes.js
var PythonShell = require('python-shell');
var config = require('../config/config');
module.exports = function(app) {
app.get('/', function(req, res) {
res.render('pages/index.ejs', {
pageTitle: 'Index'
}); // load the index.ejs file
});
app.post('/test', function(req, res) {
var options = {
mode: 'text',
pythonPath: config.pythonPath,
pythonOptions: ['-u'],
scriptPath: config.pythonScriptsDirectory
};
var pyshell = new PythonShell('printRange.py', options);
pyshell.on('message', function (message) {
console.log(message);
});
res.sendStatus(200)
});
}
Thanks in advance
socket.io is a good solution. This will take care of passing the messages from the server to the client. You just post the message to on the server side, and have a callback react to it on the client side
On the server side you'll have something like this:
var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
io.on('connection', function(){
console.log('connected');
});
server.listen(3000);
...
var pyshell = new PythonShell('printRange.py', options);
pyshell.on('message', function (message) {
if (connected)
io.emit('logentry',message);
});
On the client side, you'll have something like this:
<script src='/socket.io/socket.io.js'></script>
<script>
var socket = io();
socket.on('logentry',function(log_entry){
add_to_html(log_entry); // You need to implement this function.
});
</script>

Categories

Resources