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
Related
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.
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>
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'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.
I am using Nginx+uWSGI+Flask to build up a web service API.
I follow http://flask.pocoo.org/docs/0.10/deploying/uwsgi/ as below
In Nginx, I want Flask handle all request appapi, and others handle by nginx.
ex.
http://www.example.com/appapi/query?name=123 will be handled by flask
http://www.example.com/ will be handled by nginx.
I add below configuration to let flask handle.
location = /appapi { rewrite ^ /appapi /; }
location /appapi { try_files $uri #appapi ; }
location #appapi {
include uwsgi_params;
uwsgi_param SCRIPT_NAME /appapi;
uwsgi_modifier1 30;
uwsgi_pass 127.0.0.1:3301;
}
uWSGI has listen 3301 port, and will load flask app, In Flask app code. I have defined route for appapi
#app.route('/appapi/query', methods=['GET'])
def query():
print 'query()'
But I find query function is not called, and in log. it return 404, not found.
Thanks in advance!
You can do this:
#app.route('/query', methods=['GET'])
def query():
print 'query()'
Then in the Nginx config:
location /appapi/ {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3301;
}