How to write the flask app.route if I have multiple parameters in the URL call?
Here is my URL I am calling from AJax:
http://0.0.0.0:8888/createcm?summary=VVV&change=Feauure
I was trying to write my flask app.route like this:
#app.route('/test/<summary,change>', methods=['GET']
But this is not working. Can anyone suggest me how to mention the app.route?
The other answers have the correct solution if you indeed want to use query params. Something like:
#app.route('/createcm')
def createcm():
summary = request.args.get('summary', None)
change = request.args.get('change', None)
A few notes. If you only need to support GET requests, no need to include the methods in your route decorator.
To explain the query params. Everything beyond the "?" in your example is called a query param. Flask will take those query params out of the URL and place them into an ImmutableDict. You can access it by request.args, either with the key, ie request.args['summary'] or with the get method I and some other commenters have mentioned. This gives you the added ability to give it a default value (such as None), in the event it is not present. This is common for query params since they are often optional.
Now there is another option which you seemingly were attempting to do in your example and that is to use a Path Param. This would look like:
#app.route('/createcm/<summary>/<change>')
def createcm(summary=None, change=None):
...
The url here would be:
http://0.0.0.0:8888/createcm/VVV/Feauure
With VVV and Feauure being passed into your function as variables.
You can try this:
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)
console output
{'a': u'hello', 'b': u'world'}
P.S. Don't omit double quotation(" ") with curl option, or it not work in Linux cuz "&"
Routes do not match a query string, which is passed to your method directly.
from flask import request
#app.route('/createcm', methods=['GET'])
def foo():
print request.args.get('summary')
print request.args.get('change')
#app.route('/createcm', methods=['GET'])
def foo():
print request.args.get('summary')
print request.args.get('change')
In your requesting url: http://0.0.0.0:8888/createcm?summary=VVV&change=Feauure, the endpoint is /createcm and ?summary=VVV&change=Feauure is args part of request. so you can try this:
from flask import Flask, request, jsonify
app = Flask(__name__)
#app.route('/createcm', methods=['get'])
def create_cm():
summary = request.args.get('summary', None) # use default value repalce 'None'
change = request.args.get('change', None)
# do something, eg. return json response
return jsonify({'summary': summary, 'change': change})
if __name__ == '__main__':
app.run(debug=True)
httpie examples:
http get :5000/createcm summary==vvv change==bbb -v
GET /createcm?summary=vvv&change=bbb HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:5000
User-Agent: HTTPie/0.9.8
HTTP/1.0 200 OK
Content-Length: 43
Content-Type: application/json
Date: Wed, 28 Dec 2016 01:11:23 GMT
Server: Werkzeug/0.11.13 Python/3.6.0
{
"change": "bbb",
"summary": "vvv"
}
You're mixing up URL parameters and the URL itself.
You can get access to the URL parameters with request.args.get("summary") and request.args.get("change").
Simply we can do this in two stpes:
1] Code in flask [app.py]
from flask import Flask,request
app = Flask(__name__)
#app.route('/')
def index():
return "hello"
#app.route('/admin',methods=['POST','GET'])
def checkDate():
return 'From Date is'+request.args.get('from_date')+ ' To Date is '+ request.args.get('to_date')
if __name__=="__main__":
app.run(port=5000,debug=True)
2] Hit url in browser:
http://127.0.0.1:5000/admin?from_date=%222018-01-01%22&to_date=%222018-12-01%22
in flak we do in this way
#app.route('/createcm')
def createcm():
summary = request.args.get('summary', type=str ,default='')
change = request.args.get('change',type=str , default='')
now you can run your end-point with different optional paramters like below
http://0.0.0.0:8888/createcm?summary=VVV
or
http://0.0.0.0:8888/createcm?change=Feauure
or
http://0.0.0.0:8888/createcm?summary=VVV&change=Feauure
Related
I am trying to parse "#" symbol as a direct url in Flask project. The issue is everytime the url is requested, it breaks any value that has # init as it's a special character in url encoding.
localhost:9999/match/keys?source=#123&destination=#123
In flask, I am trying to get these arguments like this
app.route(f'/match/keys/source=<string:start>/destination=<string:end>', methods=['GET'])
The url response that i see on console is this:
"GET /match/keys/source=' HTTP/1.0" 404 -] happens
I believe you might not fully understand how 'query strings' work in flask. This url:
app.route(f'/match/keys/source=<string:start>/destination=<string:end>', methods=['GET'])
won't work as you expect as it won't match the request:
localhost:9999/match/keys?source=#123&destination=#123
rather it aught to be:
#app.route('/match/keys', methods=['GET'])
and this would match:
localhost:9999/match/keys?source=%23123&destination=%23123
Then to catch those 'query strings' you do:
source = request.args.get('source') # <- name the variable what you may
destination = request.args.get('destination') # <- same as the naming format above
So when you call localhost:9999/match/keys?source=%23123&destination=%23123 you test for those 'query strings' in the request url and if they are they that route function would execute.
I wrote this test:
def test_query_string(self):
with app.test_client() as c:
rc = c.get('/match/keys?source=%23123') # <- Note use of the '%23' to represent '#'
print('Status code: {}'.format(rc.status_code))
print(rc.data)
assert rc.status_code == 200
assert 'source' in request.args
assert rc.data.decode('utf-8') == "#123"
and it passes using this route function:
#app.route('/match/keys', methods=['GET'])
def some_route():
s = request.args.get('source')
return s
So you see I was able to catch the query string source value in my unit test.
I found another trick to work around with it. Instead of using GET method, I switched to POST
localhost:9999/match/keys
and in the app.routes, i sent the argument to get_json.
app.route('/match/keys/',method=['POST'])
def my_func():
arg = request.get_json
In postman, I send the POST request and send the body to be like this:
Postman Post request
I wish to show my data in a webpage by using flask. (Trying to learn it)
from flask import Flask, jsonify, make_response
from flask_cors import CORS
api = Flask(__name__)
CORS(api)
api.config['JSON_AS_ASCII'] = False
api.config["JSON_SORT_KEYS"] = False
#api.route('/token',methods=["POST"])
def get_token(self):
data = {
"type": "testing",
}
response1 = make_response(jsonify(data))
return response1
if __name__ == "__main__":
api.run(port=11111)
current output when try http://127.0.0.1:11111/ on google chrome:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
I also tried with /token:
Method Not Allowed
The method is not allowed for the requested URL.
you need to go to http://127.0.0.1:11111/token, if you want to go to http://127.0.0.1:11111/ you need to define a function with route #api.route('/',methods=["POST"])
Also a browser makes GET requests via URL unless explicitly defined, change it to get via #api.route('/',methods=["GET"])
Your route is /token, so you need to go to http://127.0.0.1:11111/token.
POST requests cannot be directly viewed in browser. Try some rest API client like Postman to test your POST request.
Alternatively, if you want to test if the API just works, change the POST method to GET. Then if you visit http://127.0.0.1:11111/token, you can see the response. Also you don't need 'self' argument to your method.
You restrict your app.route to only POST. If you want to enter your page from url you have to specify GET as well.
Read about http requests
from flask import Flask, jsonify, make_response
from flask_cors import CORS
api = Flask(__name__)
CORS(api)
api.config['JSON_AS_ASCII'] = False
api.config["JSON_SORT_KEYS"] = False
#api.route('/token',methods=["GET", "POST"])
def get_token(self):
data = {
"type": "testing",
}
response1 = make_response(jsonify(data))
return response1
if __name__ == "__main__":
api.run(port=11111)
I am currently trying to write some unit tests for my Flask application. In many of my view functions (such as my login), I redirect to a new page. So for example:
#user.route('/login', methods=['GET', 'POST'])
def login():
....
return redirect(url_for('splash.dashboard'))
I'm trying to verify that this redirect happens in my unit tests. Right now, I have:
def test_register(self):
rv = self.create_user('John','Smith','John.Smith#myschool.edu', 'helloworld')
self.assertEquals(rv.status, "200 OK")
# self.assert_redirects(rv, url_for('splash.dashboard'))
This function does make sure that the returned response is 200, but the last line is obviously not valid syntax. How can I assert this? My create_user function is simply:
def create_user(self, firstname, lastname, email, password):
return self.app.post('/user/register', data=dict(
firstname=firstname,
lastname=lastname,
email=email,
password=password
), follow_redirects=True)
Flask has built-in testing hooks and a test client, which works great for functional stuff like this.
from flask import url_for, request
import yourapp
test_client = yourapp.app.test_client()
with test_client:
response = test_client.get(url_for('whatever.url'), follow_redirects=True)
# check that the path changed
assert request.path == url_for('redirected.url')
For older versions of Flask/Werkzeug the request may be available on the response:
from flask import url_for
import yourapp
test_client = yourapp.app.test_client()
response = test_client.get(url_for('whatever.url'), follow_redirects=True)
# check that the path changed
assert response.request.path == url_for('redirected.url')
The docs have more information on how to do this, although FYI if you see "flaskr", that's the name of the test class and not anything in Flask, which confused me the first time I saw it.
Try Flask-Testing
there is api for assertRedirects you can use this
assertRedirects(response, location)
Checks if response is an HTTP redirect to the given location.
Parameters:
response – Flask response
location – relative URL (i.e. without http://localhost)
TEST script:
def test_register(self):
rv = self.create_user('John','Smith','John.Smith#myschool.edu', 'helloworld')
assertRedirects(rv, url of splash.dashboard)
One way is to not follow the redirects (either remove follow_redirects from your request, or explicitly set it to False).
Then, you can simply replace self.assertEquals(rv.status, "200 OK") with:
self.assertEqual(rv.status_code, 302)
self.assertEqual(rv.location, url_for('splash.dashboard', _external=True))
If you want to continue using follow_redirects for some reason, another (slightly brittle) way is to check for some expected dashboard string, like an HTML element ID in the response of rv.data. e.g. self.assertIn('dashboard-id', rv.data)
You can verify the final path after redirects by using Flask test client as a context manager (using the with keyword). It allows keeping the final request context around in order to import the request object containing request path.
from flask import request, url_for
def test_register(self):
with self.app.test_client() as client:
user_data = dict(
firstname='John',
lastname='Smith',
email='John.Smith#myschool.edu',
password='helloworld'
)
res = client.post('/user/register', data=user_data, follow_redirects=True)
assert res.status == '200 OK'
assert request.path == url_for('splash.dashboard')
I'm trying to get Flask to handle cross-site scripting properly. I've taken the crossdomain decorator snippet from here:
http://flask.pocoo.org/snippets/56/
In the code below, I've put the decorator snippet and the basic flask server.
I'm calling the decorator with headers='Content-Type' because otherwise I was getting "Request header field Content-Type is not allowed by Access-Control-Allow-Headers." in the browser.
So here is my question:
As-is, the code below works. But when I want to restrict to only a specific server like so:
#crossdomain(origin='myserver.com', headers='Content-Type')
I get the browser error
"Origin http://myserver.com is not allowed by Access-Control-Allow-Origin."
I can't get it working for anything other than origin='*'.
Does anybody have any ideas?
Here is the complete code:
from datetime import timedelta
from flask import make_response, request, current_app, Flask, jsonify
from functools import update_wrapper
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
app = Flask(__name__)
#app.route('/my_service', methods=['POST', 'OPTIONS'])
#crossdomain(origin='*', headers='Content-Type')
def my_service():
return jsonify(foo='cross domain ftw')
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080, debug=True)
For reference my python version is 2.7.2
Flask version is 0.7.2
I just tried the same code with python version 2.7.3 and Flask version 0.8.
With these versions, it fails with
#crossdomain(origin='myserver.com', headers='Content-Type')
but it works with
#crossdomain(origin='http://myserver.com', headers='Content-Type')
Perhaps it just doesn't work with Flask 0.7.2? (despite what it says on the snippet page).
EDIT:
After playing with this a lot more (and upgrading to Flask 0.9) it seems that the real problem (or yet another problem) might be related to having multiple allowed origins in a list. In other words, using the above code like this:
#crossdomain(origin=['http://myserver.com', 'http://myserver2.com'], headers='Content-Type')
doesn't work.
To fix this problem I tweaked the decorator. See code here:
http://chopapp.com/#351l7gc3
This code returns only the domain of the requesting site if it is in the list. Kinda quirky, but at least for me, problem solved :)
Python is trying hard to prevent you from exposing yourself to cross site scripting attacks.
One fix is by giving in, and having your requests hit the same server the flask script is running on. Fetching JSON from far away servers defined in strings is risky business anyway.
I was able to fix it by letting the browser keep itself on the same server, like this:
$('a#calculate').bind('click', function() {
$.getJSON('/_add_numbers', {
a: $('input[name="a"]').val(),
b: $('input[name="b"]').val()
}, function(data) {
$("#result").text(data.request);
});
return false;
});
Notice how getJSON method is passed a /_add_numbers. That communicates to the browser to stay on the same host and look for that page. Then the browser is happy and secure we are staying on the same host, and you never get the error:
Origin http://myserver.com is not allowed by Access-Control-Allow-Origin
There is a need to make POST request from server side in Flask.
Let's imagine that we have:
#app.route("/test", methods=["POST"])
def test():
test = request.form["test"]
return "TEST: %s" % test
#app.route("/index")
def index():
# Is there something_like_this method in Flask to perform the POST request?
return something_like_this("/test", { "test" : "My Test Data" })
I haven't found anything specific in Flask documentation. Some say urllib2.urlopen is the issue but I failed to combine Flask and urlopen. Is it really possible?
For the record, here's general code to make a POST request from Python:
#make a POST request
import requests
dictToSend = {'question':'what is the answer?'}
res = requests.post('http://localhost:5000/tests/endpoint', json=dictToSend)
print 'response from server:',res.text
dictFromServer = res.json()
Notice that we are passing in a Python dict using the json= option. This conveniently tells the requests library to do two things:
serialize the dict to JSON
write the correct MIME type ('application/json') in the HTTP header
And here's a Flask application that will receive and respond to that POST request:
#handle a POST request
from flask import Flask, render_template, request, url_for, jsonify
app = Flask(__name__)
#app.route('/tests/endpoint', methods=['POST'])
def my_test_endpoint():
input_json = request.get_json(force=True)
# force=True, above, is necessary if another developer
# forgot to set the MIME type to 'application/json'
print 'data from client:', input_json
dictToReturn = {'answer':42}
return jsonify(dictToReturn)
if __name__ == '__main__':
app.run(debug=True)
Yes, to make a POST request you can use urllib, see the documentation.
I would however recommend to use the requests module instead.
EDIT:
I suggest you refactor your code to extract the common functionality:
#app.route("/test", methods=["POST"])
def test():
return _test(request.form["test"])
#app.route("/index")
def index():
return _test("My Test Data")
def _test(argument):
return "TEST: %s" % argument