I have a very minimal API (let's call it api.py) using Flask :
from flask import Flask, request
from flask_restx import Resource, Api
from flask_cors import CORS
app = Flask(__name__)
cors = CORS(app)
api = Api(app)
#api.route('/hello')
class HelloWorld(Resource):
def get(self):
return {"hello" : "world"}
if __name__ == '__main__':
app.run(port=5000)
}`
I then run : python3 api.py, no error
On another command line, I then query the API
curl http://localhost:5000/hello
which give me the right answer : {"hello": "world"}
On its side, the Flask App says : 127.0.0.1 - - [21/May/2020 22:55:38] "GET /hello HTTP/1.1" 200 -
Which seems ok to me
I then build a JS / Ajax Query to query the API from a WebPage :
$.ajax({
url: "http://localhost:5000/hello"
, type: "GET"
, contentType: "application/json"
, dataType: "json"
, success: function(data) {
console.log(data)
}
})
When I access the Web Page that fires the Ajax call, I get the following error message :
GET http://localhost:5000/hello net::ERR_CONNECTION_REFUSED
I understand that is a CORS issue. The problem is that I did test ALL the tricks from SO and other help forums with no success...
I did try :
from flask_cors import CORS
app.run(host="0.0.0.0", debug=True)
app.run(host="0.0.0.0", debug=False)
#cross-origin
...
Nothing works, I still have this ERR_CONNECTION_REFUSED
Thanks for any help on that subject, as I am loosing my head with that problem...
Your ajax call shouldn't be pointing to localhost. Replace it with the URL of the EC2 instance.
No need to configure Nginx to listen on your app's port for this task. Or exposing this port outside on the remote host.
But yes, for remote server setup ajax call shouldn't be pointing to the localhost. You need to update it to the url you see in the browser. So either machine IP or a DNS.
While for local machine debugging you could try 127.0.0.1 instead of localhost.
The second part of making this work is to deal with CORS:
for remote server CORS should be enabled both in the app (as middleware) and in the preflight request (see how to allow CORS for OPTIONS requests);
for local machine I would recommend disabling CORS by running the browser in disabled security mode (like disable-web-security flag for Chrome etc.).
Related
I deployed my Flask App on Heroku, I noticed that once deployed post request in the API don't work anymore and the app just responds with status code 400 and "message": "The browser (or proxy) sent a request that this server could not understand.". Get requests work normally but post don't. Also I am quite confused because if I run the app on localhost everything works as expected but not once deployed. Anyone knows how to solve?
To parse request argument I use args = ParseArgs.parse_args() from flask_restful and I retrive the value with:
args = ParseArgs.parse_args()
mode = args['Mode']
value = args['Value']
I tried to look up on google and I saw that it might be the Content-Type header with value application/json, I removed it but the problem still persists.
I don't want to use getUpdates method to retrieve updates from Telegram, but a webhook instead.
Error from getWebhookInfo is:
has_custom_certificate: false,
pending_update_count: 20,
last_error_date: 1591888018,
last_error_message: "SSL error {error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed}"
My code is:
from flask import Flask
from flask import request
from flask import Response
app = Flask(__name__)
#app.route('/', methods=['POST', 'GET'])
def bot():
if request.method == 'POST':
return Response('Ok', status=200)
else:
return f'--- GET request ----'
if __name__ == "__main__":
app.run(host='0.0.0.0', port='8443', debug=True, ssl_context=('./contract.crt', '.private.key'))
When I hit https://www.mydomain.ext:8443/ I can see GET requests coming but not POST ones when I write something on my telegram-bot chat
Also that's how I set a webhook for telegram as follow:
https://api.telegram.org/botNUMBER:TELEGRAM_KEY/setWebhook?url=https://www.mydomain.ext:8443
result:
{
ok: true,
result: true,
description: "Webhook was set"
}
Any suggestion or something wrong I've done?
https://core.telegram.org/bots/api#setwebhook
I'm wondering if the problem it's caused because I'm using 0.0.0.0, the reason it's that if I use 127.0.0.0 the url/www.mydomain.ext cannot be reached
Update
ca_certitificate = {'certificate': open('./folder/ca.ca-bundle', 'rb')}
r = requests.post(url, files=ca_certitificate)
print(r.text)
that print gives me:
{
"ok": false,
"error_code": 400,
"description": "Bad Request: bad webhook: Failed to set custom certificate file"
}
I deployed a Telegram chatbot without Flask a while ago.
I remember that the POST and GET requests required /getUpdates and /sendMessage added to the bot url. Maybe it will help.
Telegram bots only works with full chained certificates. And the error in your getWebHookInfo:
"last_error_message":"SSL error {337047686, error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed}"
Is Telegram saying that it needs the whole certificate chain (it's also called CA Bundle or full chained certificate). as answered on the question.
If you validate your certificate using the SSLlabs you will see that your domain have chain issues:
https://www.ssllabs.com/ssltest/analyze.html?d=www.vallotta-party-bot.com&hideResults=on
To solve this need you need to set the CA Certificate. In this way, you need to find the CA certificate file with your CA provider.
Also, the best option in production sites is to use gunicorn instead of Flask.
If you are using gunicorn, you can do this with command line arguments:
$ gunicorn --certfile cert.pem --keyfile key.pem --ca_certs cert.ca-bundle -b 0.0.0.0:443 hello:app
Or create a gunicorn.py with the following content:
import multiprocessing
bind = "0.0.0.0:443"
workers = multiprocessing.cpu_count() * 2 + 1
timeout = 120
certfile = "cert/certfile.crt"
keyfile = "cert/service-key.pem"
ca_certs = "cert/cert.ca-bundle"
loglevel = 'info'
and run as follows:
gunicorn --config=gunicorn.py hello:app
If you use Nginx as a reverse proxy, then you can configure the certificate with Nginx, and then Nginx can "terminate" the encrypted connection, meaning that it will accept encrypted connections from the outside, but then use regular unencrypted connections to talk to your Flask backend. This is a very useful setup, as it frees your application from having to deal with certificates and encryption. The configuration items for Nginx are as follows:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# ...
}
Another important item you need to consider is how are clients that connect through regular HTTP going to be handled. The best solution, in my opinion, is to respond to unencrypted requests with a redirect to the same URL but on HTTPS. For a Flask application, you can achieve that using the Flask-SSLify extension. With Nginx, you can include another server block in your configuration:
server {
listen 80;
server_name example.com;
location / {
return 301 https://$host$request_uri;
}
}
A good tutorial of how setup your application with https can be found here: Running Your Flask Application Over HTTPS
I had similar case. I was developing bot on localhost (yet without SSL) and tunneled it to web through ngrok. In beginning all was OK, but once I found no POST-requests are coming. It turned out time of tunneling expired. I laughed and restarted tunneling. But requests weren't coming. It turned out, I forgot to change address of webhook (it switches every ngrok session). Don't repeat my errors.
I am creating an app with stack React => Flask => MongoDB.
I want to have an easy to use development environment, so I host everything locally.
I work in Ubuntu 16
Running flask app from PyCharm on localhost:5000.
Writing React app with VS Code and running it with console npm start command, hosting it on localhost:3000.
I want to make a GET call from React app to Flask web api to retrieve some data from db into frontend.
Flask code:
app = Flask(__name__)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
client = MongoClient('localhost', 27017)
db = client['test-database']
collection = db['test-collection']
#app.route("/orders/")
#cross_origin()
def GetAllOrders():
all_docs = list(collection.find({}, {'_id': False}))
print(all_docs)
return jsonify(all_docs)
React code:
componentDidMount() {
console.log(4);
fetch("http://localhost:5000/orders/", { mode: "no-cors" })
.then(results => {
console.log(5);
console.log(results);
console.log(results.body);
});
}
So whether I set mode "no-cors" or not the Chrome Developer Tools Network tab shows the GET call as successful and I can see the orders. Meanwhile, in the Console tab
when I send GET with mode: "no-cors" option I get Response object that has properties bodyUsed: false and body: null, so can not display the orders;
when I send GET without mode: "no-cors" option I get error:
Failed to load http://localhost:5000/orders/: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:3000, *', but only one is allowed. Origin 'http://localhost:3000' is therefore not allowed access. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
And Network tab inspection shows only value for 'Access-Control-Allow-Origin' header. Its value is 'http://localhost:3000'.
What am I missing? How to get those orders into my React application?
PS. I have CORS Chrome plugin installed and enabled.
Just had to disable the CORS chrome plugin. That plugin was the thing that was duplicating Access-Control-Allow-Origin header values.
I do not understand the setup below used to work but it suddenly broke.
I configured CORS on Falcon as follow (I run it via gunicorn):
cors = CORS(
allow_all_origins=True,
allow_all_headers=True,
allow_all_methods=True
)
api = application = falcon.API(
middleware=[
cors.middleware,
auth_middleware,
sqlalchemy_middleware
]
)
On AngularJS
return this.authHttp.get(CONFIG.apiUrl + '/user').map(
(res: Response) => res.json());
Using http.get with manually setting the token does not work.
I have access to my token in localStorage.getItem('token').
I have got the following errors:
zone.js:2019 OPTIONS http://0.0.0.0:8000/user 401 (Unauthorized)
XMLHttpRequest cannot load http://0.0.0.0:8000/user. Response for preflight has invalid HTTP status code 401
I searched everywhere on the internet and I understand it is a server issue... However the server used to work with that configuration.
Let me know if you have any idea.
Cheers,
The solution was simple, I needed to disable the authentication from the FalconAuthMiddleware on 'OPTIONS' requests in my controller.
auth = {
'exempt_methods': ['OPTIONS']
}
i'm learning to use python flask to complement angularJS. This is what i have on flask:
#!flask/bin/python
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/hola', methods=['GET'])
def get_tasks():
x="pepepepep"
return jsonify({'msj': x})
if __name__ == '__main__':
app.run(debug=True)
when i open in my browser:
http://127.0.0.1:5000/hola
i do indeed see the json object with that random message.
Now, when i go to angularJS, this is what i have on the controller:
function ControladorPrueba($scope,$http) {
$scope.mensaje="i";
$scope.ObtenerMsj= function(){
$http.get('http://localhost:5000/hola')
.success(function(data){
$scope.mensaje=data.msj;
})
.error(function(data,status){
$scope.mensaje = status;
});
};
}
The problem is that when this function is executed, the get always go on .error(...), any ideas of why this happens even when the service in flask works well when opened on the browser? am i missing something?
Any help appreciated. Thanks!
I suspect that this may be due to the Same-origin policy. If you're running your Flask app on localhost, and if you're serving your Angularjs files separately (for example, using the file:/// protocol), then the browser will actually forbid your Angular code from talking to the webservice for security reasons.
If this is the case, you have two solutions. You can either start serving your HTML/CSS/Javascript files from your Flask app, or by using something like Flask-Cors to explicitly set your server to allow external requests.