Mapping URL path parameters with Flask - python

I am trying to map the path parameter form part of the request URL:
example
I am doing this but it doesn't work:
#app.route("/people/:<string:id>", methods=['GET'])
def api_search_a_person(id):
return Id
Does anyone know how to get the value after the ":" (string "123456-7" in the example)

: is a special URI character because it's used to define the protocol or port, so it's likely that your browser URI encodes the character.
Do you really need this character anyway ? Can't you simply remove it (http://host.net/people/123456-7), or use URI parameters instead http://host.net/people/?123456-7 ?
If you really want to use a :, escape it on both ends (the escape character is %3A).

Try this:
#app.route("/people/:<string_id>", methods=['GET']) # notice _ instead :
def api_search_a_person(string_id): # extracts the string_id from the request url
return string_id # returns the mapped string_id
It's unclear why you used <string:id> with : inside.
And also what Id represents in the return (maybe you meant id from the method parameter ...

Related

Python: How to only URL Encode a specific URL Parameter?

I have some big URLs that contain a lot of URL parameters.
For my specific case, I need to URL Encode the content of one specific URL Parameter (q) when the content after the "q=" starts with a slash ("/")
Example URL:
https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=/"TEST"/"TEST"
How can I only URL encode that last part of the URL which is within the "q" parameter?
The output of this example should be:
https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=%2F%22TEST%22%2F%22TEST%22%20
I already tried some different things with urllib.parse but it doesnt work the way I want it.
Thanks for your help!
split the string on the &q=/ part and only encode the last string
from urllib import parse
url = 'https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=/"TEST"/"TEST"'
encoded = parse.quote_plus(url.split("&q=/")[1])
encoded_url = f"{url.split('&q=/')[0]}&q=/{encoded}"
print(encoded_url)
output
https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=%2F%22TEST%22%2F%22TEST%22
Note that there's a difference between this and the requested output, but you have an url encoded space (%20) at the end
EDIT
Comment shows a different need for the encoding, so the code needs to change a bit. The code below only encodes the part after &q=. Basically, first split the url and the parameters, then iterate through the parameters to find the q= parameter, and encode that part. Do some f-string and join magic and you get an url that has the q parameter encoded. Note that this might have issues if an & is present in the part that needs to be encoded.
url = 'https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=/"TEST"/"TEST"&utm_source=test1&cpc=123&gclid=abc123'
# the first parameter is always delimited by a ?
baseurl, parameters = url.split("?")
newparameters = []
for parameter in parameters.split("&"):
# check if the parameter is the part that needs to be encoded
if parameter.startswith("q="):
# encode the parameter
newparameters.append(f"q={parse.quote_plus(parameter[2:])}")
else:
# otherwise add the parameter unencoded
newparameters.append(parameter)
# string magic to create the encoded url
encoded_url = f"{baseurl}?{'&'.join(newparameters)}"
print(encoded_url)
output
https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=%2F%22TEST%22%2F%22TEST%22&utm_source=test1&cpc=123&gclid=abc123
EDIT 2
Trying to solve the edge case where there's a & character in the string to be encoded, as this messes up the string.split("&").
I tried using urllib.parse.parse_qs() but this has the same issue with the & character. Docs for reference.
This question is a nice example of how edge cases can mess up simple logic and make it overly complicated.
The RFC3986 also didn't specify any limitations on the name of the query string, otherwise that could've been used to narrow down possible errors even more.
updated code
from urllib import parse
url = 'https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=/"TEST"/&"TE&eeST"&utm_source=test1&cpc=123&gclid=abc123'
# the first parameter is always delimited by a ?
baseurl, parameters = url.split("?")
# addition to handle & in the querystring.
# it reduces errors, but it can still mess up if there's a = in the part to be encoded.
split_parameters = []
for index, parameter in enumerate(parameters.split("&")):
if "=" not in parameter:
# add this part to the previous entry in split_parameters
split_parameters[-1] += f"&{parameter}"
else:
split_parameters.append(parameter)
newparameters = []
for parameter in split_parameters:
# check if the parameter is the part that needs to be encoded
if parameter.startswith("q="):
# encode the parameter
newparameters.append(f"q={parse.quote_plus(parameter[2:])}")
else:
# otherwise add the parameter unencoded
newparameters.append(parameter)
# string magic to create the encoded url
encoded_url = f"{baseurl}?{'&'.join(newparameters)}"
print(encoded_url)
output
https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=%2F%22TEST%22%2F%26%22TE%26eeST%22&utm_source=test1&cpc=123&gclid=abc123
#EdoAkse has a good answer, and should get the credit for the answer.
But the purist in me would do the same thing slightly differently, because
(1) I don't like doing the same function on the same data twice (for efficiency), and
(2) I like the logical symmetry of using the join function to reverse a split.
My code would look more like this:
from urllib import parse
url = 'https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=/"TEST"/"TEST"'
splitter = "&q=/"
unencoded,encoded = url.split(splitter)
encoded_url = splitter.join(unencoded,parse.quote_plus(encoded))
print(encoded_url)
Edit: I couldn't resist posting my edited answer based on the commentary. You can see the virtual identical code developed independently. This must be the right approach then, I guess.
from urllib import parse
url = 'https://www.exmple.com/test?test1=abc&test2=abc&test3=abc&q=/"TEST"/"TEST"'
base_url,arglist = url.split("?",1)
args = arglist.split("&")
new_args = []
for arg in args:
if arg.lower().startswith("q="):
new_args.append(arg[:2]+parse.quote_plus(arg[2:]))
else:
new_args.append(arg)
encoded_url = "?".join([base_url,"&".join(new_args)])
print(encoded_url)

I'm not understanding what "Version" v I have to put as a format of date

I'm using the FOURSQUARE API for extracting the venue searches. I have created a URL with my client_id and client_secret. But I'm unable to know what VERSION DATE I need to put.
Please check the error in the image. I checked online but can't quite understand it. Any help will be appreciated.
First of All:
Go revoke and regenerate your token immediately, since you just posted it to the internet
Your URL only contained 3 format variables (count the {} characters in your format string)
You tried to stuff 4 variables into a format string containing 3 holes.
.format(
venue_id, # gets placed in the url after client_id=
CLIENT_ID, # gets placed after client_secret=
CLIENT_SECRET, # placed after v=
VERSION # placed nowhere because you don't have a 4th {} in the string.
)
The error result you are seeing shows your CLIENT_SECRET is an invalid version.
You may be violating their TOS by publishing your client keys, that is why you should revoke and regenerate.
Suggestion
use named format strings
if you used named strings, then you will reduce your chance to make mistakes like this because if a required name isn't passed in you'll get an error, if you pass in extra, no problem, but you won't get this config shifting error.
url = "https://<stuff>/client_id={client_id}&client_secret={client_secret}&v={version}".format(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
version=VERSION
)
or shorthand format strings where the variables inside the format string are named variables in your program.
url = f"https://<stuff>/client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&v={VERSION}"
# ^
#-----| # f indicates this is an inline format string

handle '&' in url parameter DJANGO

I am creating an api in django in which I have a parameter (state_name) and the value of state_name can include '&' in its value which is causing certain problems ?
For example if my url is like
http://localhost:8080/api?state_name=jammu&kashmir&value=2
so in the above example when i try to fetch the values from the url it takes the value of state_name only 'jammu' and treats kashmir as a parameter?
What can i do to resolve this issue in django ?
You need to escape the & for use in http query parameters.
http://localhost:8080/api?state_name=jammu%26kashmir&value=2
If you're posting this from another Python script, you can use the urllib.parse.quote function to do it for you:
from urllib import parse
parse.quote("jammu&kashmir") # jammu%26kashmir
I am creating an api in django in which I have a parameter (state_name) and the value of state_name can include '&' in its value which is causing certain problems?
This is nonsensical. In a querystring, two parameters are separated by an ampersand (&). For example:
foo=bar&qux=3
If you want the content to contain an ampersand, you need to encode it, like:
?state_name=jammu%26kashmir&value=2
Here %26 is the encoding of the ampersand, and then the querystring has two parameters: state_name and value. These are then parsed like:
>>> from django.http import QueryDict
>>> QueryDict('state_name=jammu%26kashmir&value=2')
<QueryDict: {'state_name': ['jammu&kashmir'], 'value': ['2']}>
You can use a QueryDict to construct such query, for example:
>>> qd = QueryDict(mutable=True)
>>> qd['state_name'] = 'jammu&kashmir'
>>> qd['value'] = '2'
>>> qd.urlencode()
'state_name=jammu%26kashmir&value=2'

django how to pass parameter from url to view

I'm trying to append a parameter no_rep at the end of my question url as a signal to show different views. I'm using Django 1.8 and following the url pattern from askbot.
This is the url.py:
url(
(r'^%s(?P<id>\d+)/' % QUESTION_PAGE_BASE_URL.strip('/') +
r'(%s)?' % r'/no-rep:(?P<no_rep>\w+)'),
views.readers.question,
name='question'
),
I'm trying to show different displays depending on the value of no_rep in my url.
This is the view:
def question(request, id, no_rep):
if no_rep == '1':
request.session['no_rep'] = True
else:
request.session['no_rep'] = False
I couldn't find information on what the +,%,? do, which is probably where the problem is. Could someone explain how the regex work with the base url? When I enter the url http://localhost:8000/question6/test-question/no_rep:1, request.session['no_rep'] should be set to true, but it's not. What am I missing?
Your url looks like this after all substitutions (%): r'^question(?P<id>\d+)/(/no-rep:(?P<no_rep>\w+))?', so:
1. Missing part for test-question
2. You have odd grouping around named group no_rep (which is legal, but not recommended)
So for url like http://localhost:8000/question6/test-question/no_rep:1 your url pattern should look like this:
r'^%s(?P<id>\d+)/test-question/(?:no_rep:(?P<no_rep>\d+))?' % QUESTION_PAGE_BASE_URL.strip('/') (you can change \d+ in the last group for \w+ if you want letters to match too).
About non-capturing groups (?:...) and meaning of + and ? you can read in the documentation of Python re module.

How to make a request to the Intersango API

I'm trying to figure out what's the correct URL format for the Intersango API (which is poorly documented). I'm programming my client in C#, but I'm looking at the Python example and I'm a little confused as to what is actually being placed in the body of the request:
def make_request(self,call_name,params):
params.append(('api_key',self.api_key)) // <-- How does this get serialized?
body = urllib.urlencode(params)
self.connect()
try:
self.connection.putrequest('POST','/api/authenticated/v'+self.version+'/'+call_name+'.php')
self.connection.putheader('Connection','Keep-Alive')
self.connection.putheader('Keep-Alive','30')
self.connection.putheader('Content-type','application/x-www-form-urlencoded')
self.connection.putheader('Content-length',len(body))
self.connection.endheaders()
self.connection.send(body)
response = self.connection.getresponse()
return json.load(response)
//...
I can't figure out this piece of code: params.append(('api_key',self.api_key))
Is it some kind of a dictionary, something that gets serialized to JSON, comma delimited, or exactly how does it get serialized? What would the body look like when the parameters are encoded and assigned to it?
P.S. I don't have anything that I can run the code with so I can debug it, but I'm just hoping that this is simple enough to understand for somebody that knows Python and they would be able to tell me what's happening on that line of code.
params is a list of 2-element lists. The list would look like ((key1, value1), (key2, value2), ...)
params.append(('api_key',self.api_key)) adds another 2-element list to the existing params list.
Finally, urllib.urlencode takes this list and converts it into a propert urlencoded string. In this case, it will return a string key1=value1&key2=value2&api_key=23423. If there are any special characters in your keys or values, urlencode will %encode them. See documentation for urlencode
I tried to get the C# code working, and it kept failing with exception {"The remote server returned an error: (417) Expectation Failed."}. I finally found what the problem is. You could read about it in depth here
In short, the way to make C# access Intersango API is to add following code:
System.Net.ServicePointManager.Expect100Continue = false;
This code needs to only run once. This is a global setting, so it affects your full application, so beware that something else could break as a result.
Here's a sample code:
System.Net.ServicePointManager.Expect100Continue = false;
var address = "https://intersango.com/api/authenticated/v0.1/listAccounts.php";
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
var postBytes = Encoding.UTF8.GetBytes("api_key=aa75***************fd65785");
request.ContentLength = postBytes.Length;
var dataStream = request.GetRequestStream();
dataStream.Write(postBytes, 0, postBytes.Length);
dataStream.Close();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Piece of cake
instead of params.append(('api_key',self.api_key))
just write:
params['api_key']=self.api_key

Categories