working on a python flask app with a vue js front end. tested it my local mac book but when i try the same thing on a linux development machine, i get a ajax error. here is the set up ( directory structure below).
backend is flask app that i run with python app.py , then in the front end vue js, i have added a route and calling the backend api endpoint. not sure if there is anything different we have to do in RHEL linux.
myApp
server
app.py
requirements.txt
Client
node_modules
public
src
components
...
package.json
installed vue-cli , npm install -g #vue/cli
Vue create myapp
npm run serve
from flask import Flask
from flask_cors import CORS
# enable CORS
CORS(app, resources={r'/*': {'origins': '*'}})
app = Flask(__name__)
#app.route('/helo')
def index():
return "Hello from flask app"
if __name__ == "__main__":
app.run()
User.vue
<template>
<div>
<div class="hello">
...
</template>
<script>
import axios from "axios";
export default {
name: "Users",
data() {
return {
message: '',
},
};
},
methods: {
getUsers() {
const path = 'http://localhost:5000/hello';
axios.get(path)
.then((res) => {
this.message = msg;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
}
},
created() {
this.getUsers();
}
}
</script>
Related
I've one Electron app in which I've used python flask a server with Angular JS for all HTML front end thing. In dev environment it's working fine as I expect. But, after creating a distributable by electron forge when I'm trying to open the .exe file it showing this Error spawn python ENOENT.
Distributable creating process by electron forge is going without any issue. But, just after getting the ".exe" file when I'm trying to open the app by clicking on ".exe" file I'm getting this error.
Error Screenshot -
My Electron app files
main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
const url = require('url');
const { spawn } = require('child_process')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
win.loadFile('./dist/angular-flask-electron-app/index.html');
};
app.whenReady().then(() => {
const status = spawn("./flask_server/.venv/Scripts/python", ["./flask_server/main.py"])
status.stdout.on('data', (data) => {
console.log(`python process stdout: ${data}`);
});
status.stderr.on('data', (data) => {
console.error(`python process stderr: ${data}`);
});
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
Flask route file - main.py
import sys
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
#app.route("/")
def hello():
return "Hello World from Flask!"
if __name__ == "__main__":
app.run(host='127.0.0.1', port=5000)
Folder structure of electron app
I can guess like somehow the distributable can't able to package the python inside it. That's why, at the time of opening the file it can't able to make the connection with Python & failed to create the python environment to run the flask route file.
But don't know what to do exactly to rectify this. Need some help.
-----UPDATE-----
First I tried Electron Forge to get the distributable. But, when I start getting this error I thought maybe Electron forge is somehow not working properly. Then I tried Electron Builder to get the distributable.
I followed this article to do that - https://medium.com/jspoint/packaging-and-distributing-electron-applications-using-electron-builder-311fc55178d9
The out folder structure is like this now -
Screenshot for "dist" folder
You can use multiple services in Google App Engine and each service can be in its own language. This is usually done by having different url prefixes, so for example one url is:
myapp.com/js/*
myapp.com/python/*
However, I'm wondering if you can mix-and-match endpoints and just set them up with a dispatch file. Here is what I have so far to give an idea of what I'm trying to do:
default-multi/
app.yaml
dispatch.yaml
js/
js.yaml
package.json
server.js
py/
py.yaml
requirements.txt
main.py
And the files are:
js/js.yaml
runtime: nodejs14
service: js
js/package.json
{
"engines": {
"node": "14.x"
},
"dependencies": {
"express": "^4.17.1"
}
}
js/server.js
'use strict';
const express = require('express');
const app = express();
app.get('/js', (req, res) => {
res.status(200).send('Hello, world!').end();
});
app.listen(8080, () => {});
module.exports = app;
py/requirements.txt
Flask==2.1.3
py/py.yaml
runtime: python39
service: py
py/main.py
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/py')
def hello():
return jsonify(hello='world')
app.yaml
runtime: python39 # requires I add this ?
service: default
dispatch.yaml
dispatch:
- url: "*/py$"
service: py
- url: "*/js$"
service: js
And to dispatch:
$ gcloud app deploy app.yaml js/js.yaml py/py.yaml dispatch.yaml
Is there an actual way to accomplish what I'm trying to do above, or is it just not possible using GAE?
This is possible. Here is a working example I have now:
default_multi/
dispatch.yaml
dispatch:
- url: "*/js"
service: js
- url: "*/py"
service: default # 'py'
js/
js.yaml
runtime: nodejs14
service: js
package.json
{
"engines": {
"node": "14.x"
},
"dependencies": {
"express": "^4.17.1"
}
}
server.js
'use strict';
const express = require('express');
const app = express();
app.get('/js', (req, res) => {
res.status(200).send('Hello, world!').end();
});
app.listen(8080, () => {});
module.exports = app;
py/
py.yaml
# set this as 'default' yaml
runtime: python39
service: default
requirements.txt
Flask==2.1.3
main.py
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/py')
def hello():
return jsonify(hello='world')
And the command to run it:
$ gcloud app deploy py/py.yaml js/js.yaml dispatch.yaml
The main points here are that:
One server needs to be registered as the default service, even if the packages are split and equivalent in priority.
Only the dispatch.yaml goes at the top level. This acts as the service-router now.
I have docker-compose.yml to bring up two docker containers.
1.) front-end - reactjs - running on port 3000
2.) back-end - python (flask) - running on port 8083
The front-end is calling the python server for a POST method through the url "http://127.0.0.1:8083/api using axios, but now since these are running at two containers, how can I connect the front-end to the python server through that url?
This is my docker-compose.yml
version: "3.2"
services:
frontend:
build: ./frontend
environment:
CHOKIDAR_USEPOLLING: "true"
volumes:
- /app/node_modules
- ./frontend:/app
ports:
- 80:3000
depends_on:
- backend
backend:
build: ./backends/jananath
volumes:
- ./backends/jananath:/usr/src/app
environment:
FLASK_APP: /usr/src/app/server.py
FLASK_DEBUG: 1
FLASK_RUN_EXTRA_FILES: "/usr/src/app/banuka.txt"
ports:
- 5000:8083
And this is the server.py python server
from flask import Flask, render_template, request, url_for, redirect
import os
from os import path, walk
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
PORT = 8083
#app.route('/api', methods=['POST', 'GET'])
def upload_file():
file = request.files['file']
filename = file.filename
print(filename)
filepath = os.path.abspath(filename)
print(filepath)
response_ = {}
response_['filename'] = str(filename)
response_['filepath'] = str(filepath)
return response_
if __name__ == '__main__':
app.run(host='0.0.0.0', port=PORT, extra_files=extra_files)
And this is the axios calling a POST method from front-end to this server.py
const onFormSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("file", file);
try {
const res = await axios.post("http://127.0.0.1:8083/api", formData, { // this url should change now?
headers: {
"Content-Type": "multipart/form-data",
"Access-Control-Allow-Credentials": "true",
"crossorigin": "true",
},
});
const {filename, filepath} = res.data;
setUploadedFile({filename, filepath});
} catch (error) {
console.log(error);
}
};
I think the problem here is the URL is now changed, but I don't know how to fix that. Can someone tell me how to fix this and what is the correct URL I should hit?
Thank you!
try backend:8083 in your react app, you will need to rebuild your react image.
I have a problem witch Angular and Flask inside Docker.
Angular linking to Flask app inside docker, but when I want to make http requests to API from Angular returns error.
Error image
My Angular service for make requests.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders,Response } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import {User} from './user';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({providedIn: 'root'})
export class LoginService {
private loginUrl = "http://waiter_flask_1:5000/api/account/login";
constructor(
private http:HttpClient
) { }
loginUSer(): Observable<any> {
return this.http.post(this.loginUrl,{"username":"test","password":"test"},httpOptions).pipe(
map((response:Response)=>response)
);
}
}
My docker-compose.yml
version: '3.3'
services:
flask:
image: flask
ports:
- "5000:5000"
volumes:
- ./run.py:/app/run.py
- ./api:/app/api
links:
- database:waiter_database_1
angular:
image: angular
ports:
- "4200:4200"
volumes:
- ./docker-angular:/usr/src/app
links:
- flask:waiter_flask_1
How can I resolve this problem?
When you make the request from the Angular app, your browser is making the request, not the angular container. Therefore your host machine that you are running your browser from will try to resolve the http://waiter_flask_1:5000 host not the angular container, and since your host cannot resolve the name waiter_flask_1, the request will fail.
What you need to do is replace the url your angular app is using to make the HTTP request with a url that your host is able to resolve e.g:
http://host.ip.address:5000/api/account/login
where host.ip.address is the ip address for the host running the flask container. If, for example, you are running the flask container on the same host that you are accessing your browser, then your url can point to localhost, eg:
http://127.0.0.1:5000/api/account/login
I've built a mongo database and a Python/PyMongo/Flask script. Now I'm learning Vue so I can put that data on a webpage, but I'm having trouble understanding which ports to use in my path. I understand mongodb uses 27017, Flask uses 5000, and Vue uses 8080.
Here's my Python/PyMongo/Flask script (app.py). Since I can't give you the entire database data, I altered the 'findNames' function to just return a string.
from flask import Flask, jsonify
from flask_cors import CORS
from pymongo import MongoClient
DEBUG = True
app = Flask(__name__)
app.config.from_object(__name__)
client = MongoClient('localhost', 27017)
db = client.argodb
CORS(app)
#app.route('/searchDB', methods=['GET'])
def findNames():
return jsonify('The database is connected!')
if __name__ == '__main__':
app.run()
And on the client side, here's my Vue index.js:
import Vue from 'vue';
import Router from 'vue-router';
import searchDB from '#/components/searchDB';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/searchDB',
component: searchDB,
},
],
});
Here's the main component, App.vue:
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
And here's my component, searchDB.vue:
<template>
<div id="search-db">
<h1>Database Search</h1>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'searchDB',
data() {
return {
msg: '',
};
},
methods: {
getMessage() {
const path = 'http://localhost:5000/searchDB';
axios.get(path)
.then((res) => {
this.msg = res.data;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
},
},
created() {
this.getMessage();
},
};
</script>
<style>
</style>
You have each application running on its own port -- MongoDB running on 27017, Flask running on 5000 and Vue (npm) running on 8080. The chain of events that you'd ideally like to happen is this.
Vue uses axios to send an http request to Flask on port 5000
Flask receives the request and handles the request and recognizes that it needs to retrieve data from the database before sending a response, so it submits a query to Mongo which is running on port 27017.
Mongo returns the requested data to Flask
Flask formats the data into an appropriate http response and returns it to the requester (Vue)
The axios call receives its response and reacts to the response data, storing the data if the response was successful (e.g. status code 200 for a Get)
Since there are a lot of steps along the way that need to go correctly, it's a good idea to test in small pieces first to ensure the whole chain is working properly. I would suggest:
Test to ensure Flask is connecting to Mongo properly and can retrieve data
Next use a curl statement or Postman to check to see if your Flask webserver is handling calls to http://localhost:5000/searchDB properly. Note that if you can also take Mongo out of the equation (as you have done) for this test by just having the call return a string.
Once you are sure you can make calls to that endpoint (/searchDB) successfully, then you can start testing Vue and Axios. This is where you are likely to run into CORS issues.
If you are able to get everything working but cannot resolve the CORS issues, then another possible setup would be to stick NGINX in front of Flask and Vue. This solution involves running NGINX on its own port, let's say port 3000 and then configuring the server to proxy for ports 5000 and 8080. Then you would change your Vue/Axios code to make calls to port 3000 instead of port 5000, which avoids CORS since this would then be the same port that Vue is being served on through NGINX.