Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle several parameters containing slashes?

Tags:

python

flask

I have a Flask app to which I need to pass several parameters containing slashes. For example, I have parameter1 = "Clothes/Bottoms" and parameter2 = "Pants/Jeans". I tried doing it this way:

In my HTML/JS:

par1 = encodeURIComponent(parameter1);
par2 = encodeURIComponent(parameter2);
console.log("Par1 = ",par1," par2 = ",par2);
$.ajax({
     type:'post',
     url:'/get_data'+'/'+par1+'/'+par2,
     ....
});

and in my app.py:

 @app.route('/get_data/<path:par1>/<path:par2>/',methods=['GET','POST'])
 def get_data(par1, par2):
     print("In get_data with par1 ",par1," and par2 ",par2)
     ....

I can see from Javascript print-outs that both parameters look fine after being encoded, but then the Python print out is:

 In get_data with par1 Clothes and par2 Bottoms/Pants/Jeans

So it somehow mistakes the slash in par1's "Clothes/Bottoms" for part of the URL and shifts "Bottoms" into par2.

Is there a better way to handle multiple parameters with slashes than just adding path:?

like image 234
user3490622 Avatar asked Apr 06 '19 15:04

user3490622


2 Answers

With /get_data/<path:par1>/<path:par2>/, there's no way for Flask to "know" which slash is the separator when it receives a request like /get_data/Clothes/Bottoms/Pants/Jeans/.

If the number of slashes in par1 is fixed, you could receive the path as a single parameter and then split it into two:

@app.route('/get_data/<path:pars>/')
def get_data(pars):
     par1 = '/'.join(pars.split("/",2)[:2]) #par1 is everything up until the 2nd slash
     par2 = pars.split("/",2)[2:][0] #par2 is everything after the second slash

Otherwise, you could simply replace the separator slash with another character like:

@app.route('/get_data/<path:par1>-<path:par2>/')
like image 125
glhr Avatar answered Sep 29 '22 00:09

glhr


To understand flask (werkzeug) routing. Take aside encoding you used in JavaScript for a second.

Regex pattern used by werkzeug's path converter is [^/].*?. This allow any number of / in url path. Which means only part1 get_data/<path:par1> can accepts both get_data/Clothes/Bottoms or get_data/Clothes/Bottoms/Pants/Jeans.

You are using two path convertor in part1 and par2 which is bad as one part1 can take all slash.

Now the other issue. why even after encoding url its not working as expected.

Flask uses Werkzeug default WSGI server. And WSGI library unescaped uri before using it for routing. i.e., get_data/Clothes%2FBottoms gets converted to get_data/Clothes/Bottoms when it comes to routing. in your case router receives get_data/Clothes/Bottoms/Pants/Jeans here it takes is "Clothes" as part1 and rest as part2.

Refer to flask issue regarding this.

Solution

Double escape in JavaScript could be workaround here. path converter can also be replaced by string.

par1 = encodeURIComponent(encodeURIComponent(parameter1));
par2 = encodeURIComponent(encodeURIComponent(parameter2));

$.ajax({
     type:'post',
     url:'http://localhost:8000/get_data'+'/'+par1+'/'+par2+'/'});

And decode at flask application to get back your strings

from urllib import unquote_plus
@app.route('/get_data/<string:par1>/<string:par2>/',methods=['GET','POST'])
def get_data(par1, par2):
    print unquote_plus(par1),  unquote_plus(par1)
like image 21
maximus Avatar answered Sep 28 '22 23:09

maximus