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
Related
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.
This question already has answers here:
Python - Flask Default Route possible?
(4 answers)
Closed 7 months ago.
I have a question about Flask.
I want to use one endpoint to handle requests.
To do so, I need to take url in router like:
#app.route("/<url>", methods=['GET','POST'])
def home(url):
base_url = "https://www.virustotal.com/"
my_url = base_url + url
For example, I will sent request to my Flask app as " localhost:5000/api/v3/files/samehashvalue "
and it will combine it with virustotal url.
my_url will something like = virustotal.com/api/v3/files/samehashvalue
How can I pass /api/v3... to my router? Without using parameters like ?url=...
I'd suggest reading redirects from the Flask docs as well as url building.
With your specific example you can obtain the url from the route and pass it into your Flask function. It's then just a case of using redirect and an f string to redirect to that new url.
# http://localhost:5000/test redirects to
# https://www.virustotal.com/api/v3/files/test
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def url_redirector(path):
return redirect(f'https://www.virustotal.com/api/v3/files/{path}')
if __name__ == '__main__':
app.run(debug=True)
I am not sure if this is correct, but I assume that you can specify the path in #app.route if it is a fixed path. For example:
#app.route("/api/v3/files", methods=['GET','POST'])
def home(url):
base_url = "https://www.virustotal.com/api/v3/files/"
Then the hash value only can be passed as a parameter.
Please let me know if I misunderstood your question and I will edit this answer.
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)
I have an endpoint in my API like below:
from flask import Flask
app = Flask(__name__)
#app.route('/price/<path:url>/')
def Ex(url):
return {'urlwas':url}
app.run()
The problem is that when I call the API with this http://127.0.0.1:5000/price/https://puresourceindia.in/store/index.php/?route=product/product&product_id=479
it should return this {"urlwas":"https://puresourceindia.in/store/index.php/?route=product/product&product_id=479"}
but it returns {"urlwas":"https://puresourceindia.in/store/index.php"}
I am unable to understand what is happening here, and how to tackle this situation?
You have to URL encode the parameter, the call should become:
http://127.0.0.1:5000/price/https%3A%2F%2Fpuresourceindia.in%2Fstore%2Findex.php%2F%3Froute%3Dproduct%2Fproduct%26product_id%3D479
and the result then becomes the one expected:
{"urlwas":"https://puresourceindia.in/store/index.php/?route=product/product&product_id=479"}
See here how to URL encode your string: https://www.urlencoder.org/
This question already has an answer here:
Flask view raises TypeError: 'bool' object is not callable
(1 answer)
Closed 6 years ago.
Edit: This is not a duplicate question. I am not asking what is wrong with the code. I very clearly said I know it's throwing an error because a view must return something. I'm asking why would the tutorial provide code that is going to throw an error and then neither prepare the user for that error or use that error to teach a lesson. The answer is, the tutorial is not perfect.
Original post: I'm following the Flask quickstart tutorial at http://flask.pocoo.org/docs/0.12/quickstart/. I'm roughly 2/5 down the page when it has me do URL building. The below code is directly from the tutorial, but it throws the titular ValueError when I try to visit localhost:5000/. I know that a view must return something and that's why it's throwing the error.
My primary question is, am I missing something in the tutorial? A tutorial shouldn't result in an error unless it's trying to teach you something by that error, but there's no mention of expecting an error. Rather, it seems to indicate it should work with the below code. I don't want to push ahead on the tutorial if I've missed something basic.
from flask import Flask, url_for
app = Flask(__name__)
#app.route('/')
def index(): pass
#app.route('/login')
def login(): pass
#app.route('/user/<username>')
def profile(username): pass
with app.test_request_context():
print url_for('index')
print url_for('login')
print url_for('login', next='/')
print url_for('profile', username='John Doe')
As you can see it is on python shell and docs says:
test_request_context(): It tells Flask to behave as though it is handling a request, even though we are interacting with it through a Python shell.
Tutorial does not say write this code to a file. If you do something like this:
#test.py
from flask import Flask, url_for
app = Flask(__name__)
#app.route('/')
def index(): pass
...
if __name__ == '__main__':
app.run()
$ python test.py
/
/login
/login?next=%2F
/user/John%20Doe
So when you hit localhost:5000 on browser you will get error View function did not return a response.
My primary question is, am I missing something in the tutorial? Yes probably you are missing to see it is on python shell.
The view function in Flask should always return Response object, otherwise the View function did not return a response error occures. It seems that the code in the example is given just for refenence and is not supposed to work out of the box.
See http://flask.pocoo.org/docs/0.12/quickstart/#about-responses for more details about responses.
You will get an error accessing localhost:5000 because you are not running the app. And you are right, it should also return something.
The goal of this block:
with app.test_request_context():
print url_for('index')
print url_for('login')
print url_for('login', next='/')
print url_for('profile', username='John Doe')
is only to show what urls generated by url_for would look like.
So you can run your file with python your_file.py and see the output in the shell.