Setting status callback URL using Python - Flask - python

I am trying to use Python - Flask to test the StatusCallback in Twilio, however, I am not getting any results, not sure what I am missing. I am using ngrok as well.
This is the code:
from flask import Flask, request, abort
import logging
logging.basicConfig(level=logging.INFO)
app = Flask(__name__)
#app.route('/webhook', methods =['POST', 'GET'])
def webhook():
status=request.values.get(['CallSid', 'From', 'To', 'Direction'])
logging.info('Status: {}'.format(status))
return ('', 204)
if __name__ == '__main__':
app.run(debug=True)
When I make a call, from the image I attached, you will notice I am not getting any results. Could you please advise what I may be missing? Thanks.

Twilio developer evangelist here.
When you create a tunnel with ngrok, a URL is set up that looks like https://RANDOMSUBDOMAIN.ngrok.io, make sure you are using the entire URL, including the subdomain.
When ngrok is running there is also a dashboard you can check to ensure that requests are being made to your ngrok URLs. You can reach this dashboard at http://localhost:4040. You can also use this to check the request parameters that are being sent.
Finally, you might have trouble with request.values.get and passing an array of keys. The get method of request.values only takes a single key, not an array.
As you pointed out in the comments, you can use request.form.to_dict(flat=False) to get a dictionary of the parameters instead. If you want to destructure that further into separate variables in a single line, you can use itemgetter from the operator module, like this:
from operator import itemgetter
#app.route('/webhook', methods =['POST', 'GET'])
def webhook():
parameters=request.form.to_dict(flat=False)
CallSid, From, To, Direction = itemgetter('CallSid', 'From', 'To', 'Direction')(parameters)
logging.info('CallSid: {}'.format(CallSid))
return ('', 204)

Related

Python Flask Redirect URL with all GET parameters dynamically?

how can I dynamically pass all of the GET parameters from one url to another via flask?
This is currently what I am doing:
import os
from flask import Flask,redirect
from flask import request
from flask import url_for
app = Flask(__name__)
#app.route('/')
def hello():
return redirect(url_for("https://myurl.com", **request.args))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
I can't really do it statically. What I am trying to accomplish:
myflaskserver:5000/page?url=google.com&header=body&identity=flash -> https://myurl.com/page?url=google.com&header=body&identity=flash
myflaskserver:5000/dance?url=dance.com&function=dancer&move=quality-> https://myurl.com/dance?url=dance.com&function=dancer&move=quality-
myflaskserver:5000/quit?host=google.com&language=english&password=test1234-> https://myurl.com/quit?host=google.com&language=english&password=test1234
With minimal code, without procedurally having to use if statements, or doing it statically with GET parameters for each page.
Thank you.
Because of the way that the question is phrased, it seems that you are looking for a way to perpetuate the url parameters only. In other words, you're not asking how to perpetuate the url page path (or "routes" in flask terminology), perhaps because you already have a strategy for that in mind.
If that assumption is incorrect, see my note near the bottom of this answer.
It also seems that you prefer passing the parameters as url parameters (versus passing the same data in the request header or payload).
If both these assumptions are correct, then the following approach may work for you:
Use the query_string method of request. This method returns all the url parameters as a bytes object (meaning you probably will need to decode it to a string if you wish to join it to your new url string).
import os
from flask import Flask,redirect
from flask import request
app = Flask(__name__)
#app.route('/')
def hello():
request_parameters = request.query_string.decode('utf-8')
return redirect("https://myurl.com?{0}".format(request_parameters))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Given the first url in your example...
myflaskserver:5000/page?url=google.com&header=body&identity=flash
...the statement request.query_string.decode('utf-8') would return the string "url=google.com&header=body&identity=flash", which is then appended to your endpoint "https://myurl.com?".
Notice ? needs to be added to the endpoint before joining with request.query_string.
Passing page paths (i.e. routes)
This was not explicitly asked in the question, so I won't go into much detail. But if you need to parse the route from the request, you could use request.url_rule, which will return everything after the domain and before the url parameters.
import os
from flask import Flask,redirect
from flask import request
app = Flask(__name__)
#app.route('/hello')
def hello():
request_parameters = request.query_string.decode('utf-8')
route = request.url_rule
return redirect("https://myurl.com{0}?{1}".format(route,request_parameters))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Or you could look into flask.referrer, which will return the referring url from the request header if it is available, which it sometimes isn't, such as in cross-origin scenarios of local testing.
Sidebar
For the information of anyone passing by this post, it may be helpful to mention why the request as written in the question will fail:
When the endpoint "https://myurl.com" of url_for is called, it will fail because the method expects an endpoint (or route, in flask terminology) defined by your app (versus an external endpoint with schema and domain) when called from an active request like this.

Flask failing with apostrophes

If a ' is used within the newName header value everything works okay, however, if ’ is used, for example, the server fails to handle the request.
I'm debugging some larger code, but have created a minimalist version that is subject to the same problem.
from flask import Flask, request
app = Flask(__name__)
#app.route('/dave', methods=['POST'])
def hello():
newName=request.headers['newName']
return newName
if __name__ == '__main__':
app.run(host= '0.0.0.0', debug=True)
app.debug = True
I am using Postman to debug. If I send " David's " everything works as intended, however, if I send "David’s" it fails, and nothing seems to show up in the error log (although I may be missing a trick).
As pointed out in the comments I've neglected to realise that headers should not contain ’. Data should therefore be sent within the POST body, rather than the header.
Example: How to get data received in Flask request
Single quotes can be an issue with Python and JSON, apparently. This appears to solve that problem https://stackoverflow.com/a/36599122/5152133

Make a python script API

I have a script on python, which prints some data. The script is on Centos7, nginx.
How could I connect to the script via URL (GET query) to be able to parse the data?
You can use a framework like Django or flask to make an api out of it. I'll suggest flask since it's very light-weight, making it ideal for such small tasks.
E.g.
def your_function(input):
# do something
return output
from flask import Flask
from flask import request
app = Flask(__name__)
#app.route('/my_api')
def your_api_function():
input = request.args.get('my_query_string')
return your_function(input)
if __name__ == '__main__':
app.run(debug=True)
And then use the endpoint
/my_api?my_query_string=my_input
You can further play around with it to return JSON, take parameters from request body and so on and so forth.
Read more here http://flask.pocoo.org/

Calling Python file in an Ajax Call

So I have established a pretty decent understanding of the simple architecture of an angularjs app, calling $http and posting to a php page, and receiving data back.
What I'm wondering, is how to do the same type of function with python. Is it possible to have python act the same, with self contained script files that accept post data and echo json back?
$username = $_POST['username'];
type variable assignment at the beginning of the script, and:
echo json_encode(response);
type response.
I'm wanting to use Python for some Internal Tools for my company, as it offers better libraries for remotely running powershell scripts (as the tools are all linux hosted) and overall just has libraries that fit my needs. I'm just having a difficult time finding a concise answer to how this could be set up.
---EDIT------
So I set up a quick example using the information below.
the angular:
var app = angular.module("api");
app.controller("MainController", ["$scope","$http",MainController]);
function MainController($scope,$http){
$http.post('/api',{test: "hello"})
.then(function(response){
console.log(response.data);
})
}
The flask:
from flask import Flask, request
import json
app = Flask(__name__)
#app.route('/api', methods=['POST', 'GET'])
def api():
if request.method == 'POST':
request.data
return 'You made it' # Just so I originally could see that the flask page
if __name__ == "__main__":
app.run()
I'm getting a 404 for that URL. If I change the angular to look at 'localhost:5000/api' (where my flask app is running),it gives me the error of "Unsupported URL Type".
I am seeing when I do the first case, it tries to look at http://localhost/api , which is correct! except for the port. Which is why I tried to specify the port.
Any suggestions for a next step?
Use flask.
You could host your app on a flask "server" and return the content you'd like too with a python processing.
http://flask.pocoo.org/
Use the documentation to setup a route where you'll POST your data using jquery or whatever, then on the route you can do your python stuff and return a JSON to your angular app if you need to.
from flask import request
#app.route('/test', methods=['POST', 'GET'])
def test():
if request.method == 'POST':
print request.data['your_field']
return your_json_data

Python: How to get multiple variables from a URL in Flask?

I'm trying to get multiple arguments from a url in Flask. After reading this SO answer I thought I could do it like this:
#app.route('/api/v1/getQ/', methods=['GET'])
def getQ(request):
print request.args.get('a')
print request.args.get('b')
return "lalala"
But when I visit /api/v1/getQ/a=1&b=2, I get a TypeError: getQ() takes exactly 1 argument (0 given). I tried other urls, like /api/v1/getQ/?a=1&b=2 and /api/v1/getQ?a=1&b=2, but to no avail.
Does anybody know what I'm doing wrong here? All tips are welcome!
You misread the error message; the exception is about how getQ is called with python arguments, not how many URL parameters you added to invoke the view.
Flask views don't take request as a function argument, but use it as a global context instead. Remove request from the function signature:
from flask import request
#app.route('/api/v1/getQ/', methods=['GET'])
def getQ():
print request.args.get('a')
print request.args.get('b')
return "lalala"
Your syntax to access URL parameters is otherwise perfectly correct. Note that methods=['GET'] is the default for routes, you can leave that off.
You can try this to get multiple arguments from a url in Flask:
--- curl request---
curl -i "localhost:5000/api/foo/?a=hello&b=world"
--- flask server---
from flask import Flask, request
app = Flask(__name__)
#app.route('/api/foo/', methods=['GET'])
def foo():
bar = request.args.to_dict()
print bar
return 'success', 200
if __name__ == '__main__':
app.run(debug=True)
---print bar---
{'a': u'hello', 'b': u'world'}
P.S. Don't omit double quotation(" ") with curl option, or it not work in Linux cuz "&"
similar question Multiple parameters in in Flask approute

Categories