How to connect backend (python, flask) with frontend (html, css, javascript) - javascript

Info: for backend I'm using python with flask (for the moment it accepts http get methods) and for frontend I'm using html, css and javascript.
Problem: I'm trying to make a http request (first time I tried POST and then GET) but the browser did not allow me to do that: "Access to XMLHttpRequest at 'localhost:5000/test' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.".
What another choices do I have? (I would like some simple choices, it is just a homework).
I've tried to make http POST and GET request.
I've read that I cannot make http request from browser.
I've read that I need (for example) an apache server. - too complicated, I need something more simple.
I've tried: https://flask-cors.readthedocs.io/en/latest/
document.getElementById("btn").addEventListener('click', add);
function add()
{
const url = "localhost:5000/test";
const http = new XMLHttpRequest();
http.open("GET", url);
http.send();
http.onreadystatechange=(e)=> {
console.log(http.responseText)
}
}
from flask import Flask
from flask_cors import CORS
from flask import request
from flask import jsonify
import json
import mysql.connector
import random
import string
import time
time.sleep(3)
app = Flask(__name__)
#app.route("/test")
def test():
return "It's working"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)
I expect that in the browser console to be printed message: "It's working", but I get the error:
Access to XMLHttpRequest at 'localhost:5000/test' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
LE: Flask server is inside a docker container. Ports are mapped "5000:5000'.

If you are using same machine, you do not need to use flask-cors.
Update:
As you are using Docker you can use flask-cors to handle CORS.
I found that the AJAX calls were not correct in your JS code. const url = "localhost:5000/test"; does not provide information on request protocol.
I followed these steps to run Flask application successfully using Docker and accessing the /test endpoint using JS outside Docker.
I updated AJAX request
Added Dockerfile to run Flask application inside Docker
Build and run the Dockerfile
Get the IP address of running Docker container.
Used the IP address in AJAX call in JS code which is outside Docker.
Folder structure:
.
├── backend.py
├── Dockerfile
├── readme.md
└── requirements.txt
requirements.txt:
Flask==1.0.2
Flask-Cors==3.0.7
Dockerfile:
FROM python:3
ENV PYTHONBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/
CMD ["python", "backend.py" ]
Build Docker file:
docker build -t flask-docker .
Run Docker:
docker run -p 5000:5000 flask-docker
* Serving Flask app "backend" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
Get Docker container ID:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
69cb7d5d243a flask-docker "python backend.py" 15 minutes ago Up 15 minutes 0.0.0.0:5000->5000/tcp
Get Docker container IP address:
docker inspect --format '{{ .NetworkSettings.IPAddress }}' 69cb7d5d243a
172.17.0.2
Use this IP address in AJAX request in HTML file:
<html>
<head>
<title>Frontend</title>
</head>
<body>
<div id="data"></div>
<button type="button" id="btn">Grab data</button>
<script type="text/javascript">
document.getElementById("btn").addEventListener('click', add);
function add()
{
const api_url = "http://172.17.0.2:5000/test";
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("data").append(this.responseText);
}
};
xhttp.open("GET", api_url, true);
xhttp.send();
}
</script>
</body>
</html>
backend.py:
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
#app.route("/test")
def test():
return "It's working"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)
Output:

Add the following line below app = Flask(__name__):
CORS(app)
Check out flask-cors simple usage

Related

unable to use getCurrentPosition() in a Flask local host app

I'm working on a python flask app for practice. I want to access the getCurrentPosition() of JS to get the geolocation. However, as the app is running on http://localhost:5000/ I'm getting an error that getCurrentPosition() and watchPosition() are deprecated on insecure origins.
Is there a way that it will work on the flask localhost server?
I have found the resolution. Geolocation can only be used in HTTPS requests.
In order to convert your localhost flask app to HTTPS from HTTP, you have to use OpenSSL to create a key and certificate.
Follow the below step to set up your HTTPS environment for the flask localhost server
If you have installed Git then OpenSSL command comes in a package. Open git bash where you want to store the files.
Run the below command in Git Bash.
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
Change your app.run() method in python script to below
context = (r"{path}\certificate.pem", r"{path}\key.pem")
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=True,ssl_context=context)
path refers to the location directory where the key and certificate files are stored.

Cross origin in JavaScript import module

I have an index.html file that connect to file called main.js.
I try to import another js file into the main.js using import, but always get the following error:
Access to script at 'file:///C:/...js/main.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
I tried to open a server but it didn't solve the problem. I don't know if the problem is in my html file or js files and I really need your help.
My code goes like this:
index.html
<body>
<script type="module" src='./js/main.js'></script>
</body>
main.js
import {
double
} from './utils.js';
utils.js
export function double(n) {
return n * 2;
}
You've probably fixed the problem by now, but for anyone googling in the future, here's the solution.
The error message says that you can't access the file from origin "null", and states that you can only do these requests with certain protocol schemes. The protocol scheme that local files on your computer have is "file://", and I think the origin: "null" thing is about it not being on a website (e.g. http://a.com vs file://a/b/c).
What you need to do to fix this is start a local server. The easiest approach is to use this python one-liner. If you are on MacOSX or Linux, go ahead and use the below command:
$ cd path/to/website
$ python -m SimpleHTTPServer 8080
Serving HTTP on 0.0.0.0 port 8000 ...
If you are on Windows, you need to install Python before making the server. To do so, go to the downloads page on their website and download the latest version. To check if it is installed, run this in the command prompt:
python -V
If you get something like Python 3.9.5, you can go ahead and run the command mentioned before.
Once you have got the server started, just navigate to http://localhost:8080/ in your browser.

Fetch timeout when port-forwarding from docker container

I'm working on simple project consisting of two docker containers in docker network (bridge). The issue it that I'm getting timeout from backend when calling it from host (via port-forward to frontend)
Setup:
docker network create --driver bridge my-network
Java Spring backend providing random quotes from database under /randomQuote endpoint
docker run -it --rm --name backend --network my-network backend:1.0
Javascript frontend for displaying the received quote
docker run -it --rm --name frontend --network my-network -p 8081:8081 frontend:1.0
Frontend code for requesting the data (called on button click):
const requestQuote = async() => {
let response = await fetch(apiUrl + getRandomQuoteEndpoint)
if(handleErrors(response)){
quote = await response.json()
displayQuote()
}
}
I tried various versions for apiUrl like:
http://backend:8080
http://172.18.0.2:8080
backend:8080
172.18.0.2:8080
Backend interface:
#RequestMapping("/")
public interface QuotesRestController {
#CrossOrigin()
#GetMapping("/randomQuote")
public ResponseEntity<Quote> getRandomQuote();
}
On host http://localhost:8081/ is displaying frontend content properly but when requesting the quote I'm receiving GET http://172.18.0.2:8080/randomQuote net::ERR_CONNECTION_TIMED_OUT.
curl from frontend is returning the quotes correctly, so issue is only when I'm port-forwarding and trying to display on host.
I suspect that this is because the call to backend:8080/randomQuote is made from my host's browser which is not in docker network so it's not able to reach backend.
Is it an issue with my configuration/code? Or I should use different approach?

How to programmatically test if ngrok is already running

We have ran ngrok on a localhost PORT say http://localhost:4000. We can manually test if the ngrok is already runing or not by using the following steps:
Check if ngrok is already running:
Hit http://127.0.0.1:4040/status
If the connection happens successfully, the following visual will show up:
If the above visual is not showing, ngrok is not running at all.
Under Tunnels section, the following visual will show up:
If the above visual is not showing, ngrok is not running on PORT 4000.
To start ngrok on http://localhost:4000, we need to run ngrok http 4000. After running this command, the above visuals will show up.
Is there some programmatic way to determine if ngrok is already running on the port?
For others stumbling by this question,
Make a request to http://localhost:4040/api/tunnels using curl or any request library in the programming language of your choice. It returns a JSON formatted response of the ngrok tunnels and their urls, which you can obtain.. If it does not return anything, it means ngrok is not running
Eg for python:
import requests
import json
from bs4 import BeautifulSoup
req = requests.get('http://127.0.0.1:4040/api/tunnels')
soup = BeautifulSoup(req.text, 'lxml')
tunnelsjson = json.loads(soup.find('p').text)
url = tunnelsjson['tunnels'][0]['public_url']
print(url)
Run this command to see using ports:
sudo lsof -PiTCP -sTCP:LISTEN
You can check it by python:
import socket;
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex(('127.0.0.1',4000))
print(result)
if result == 0:
print "Port is not open"
else:
print "Port is open"
P.S. if port is in use, result is 0, if not it is 61.
Run netstat -tulnap | grep ngrok
netstat -tulnap shows which processes are running on the machine
grep ngrok filters for the ngrok processes.

Calling flask via javascript run on an https external site

There is an external site that runs https. I have a UI based "server" that I can run javascript from.
I want to have that site send me a XMLHttpRequest() via my javascript running on their site to my Flask_Restful running elsewhere.
This code works, but only on http sites. Their site is https.
I am currently running this code on the chrome console when on their site.
var xhttp = new XMLHttpRequest();
xhttp.open("PUT", "http://[my_server]:5000/todos/todo3", true);`
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhttp.send(JSON.stringify({ "task": "new task2"}));
So I get the error:
This request has been blocked; the content must be served over HTTPS.
If I run it with https I get the error:
OPTIONS https://9[my_flask_server]:5000/todos/todo3 net::ERR_SSL_PROTOCOL_ERROR
My Flask server also prints out:
code 400, message Bad request syntax ('\x16...
How do I enable my flask site to allow this SSL call?
I have enabled CORS like this:
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app)
api = Api(app)
EDIT 1
I am trying to add ssl capabilities to my server.
I have made a host.cert file and a host.key file by using this:
openssl genrsa 1024 > host.key
chmod 400 host.key
openssl req -new -x509 -nodes -sha1 -days 365 -key host.key -out host.cert
source: https://serverfault.com/questions/224122/what-is-crt-and-key-and-how-can-i-generate-them
And I updated my Flask app.py such that the top looks like this:
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource
from flask_cors import CORS, cross_origin
from OpenSSL import SSL
context = SSL.Context(SSL.SSLv23_METHOD)
context.use_privatekey_file('host.key')
context.use_certificate_file('host.cert')
app = Flask(__name__)
CORS(app)
api = Api(app)
source: http://flask.pocoo.org/snippets/111/
When I run python app.py it errors out now.
Current error:
Traceback (most recent call last):
File "app.py", line 72, in <module>
app.run(host="[my_server]", port="5000", ssl_context=context)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 841, in run
run_simple(host, port, self, **options)
File "/usr/local/lib/python2.7/dist-packages/werkzeug/serving.py", line 742, in run_simple
inner()
File "/usr/local/lib/python2.7/dist-packages/werkzeug/serving.py", line 702, in inner
fd=fd)
File "/usr/local/lib/python2.7/dist-packages/werkzeug/serving.py", line 596, in make_server
passthrough_errors, ssl_context, fd=fd)
File "/usr/local/lib/python2.7/dist-packages/werkzeug/serving.py", line 528, in __init__
self.socket = ssl_context.wrap_socket(sock, server_side=True)
AttributeError: 'OpenSSL.SSL.Context' object has no attribute 'wrap_socket'
EDIT 2: (didn't work)
sudo pip install 'Werkzeug==0.9.6'
Changes the issue to:
Traceback (most recent call last):
File "/usr/lib/python2.7/logging/__init__.py", line 851, in emit
msg = self.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 724, in format
return fmt.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 464, in format
record.message = record.getMessage()
File "/usr/lib/python2.7/logging/__init__.py", line 328, in getMessage
msg = msg % self.args
TypeError: %d format: a number is required, not str
Logged from file _internal.py, line 87
EDIT 3: PROGRESS
Using just
from OpenSSL import SSL
...
and
...
if __name__ == '__main__':
context = ('host.crt', 'host.key')
app.run(host...
Has changed the console side error from:
OPTIONS https://[my_flask_server]:5000/todos/todo3 net::ERR_SSL_PROTOCOL_ERROR
to
OPTIONS https://[my_flask_server]:5000/todos/todo3 net::ERR_INSECURE_RESPONSE
EDIT 3
At this point its just browser related security checks since my ssl certificate is not recognized / weak. I think I've got it working as well as I can, Im gonna try and get it more authorized i guess?

Categories