4

I am using Django Rest Framework.

The API receives GET requests with json objects encoded into the URL. For example:

/endpoint?%7B%0D%0A++%22foo%22%3A%5B%22bar%22%2C%22baz%22%5D%0D%0A%7D

Where the decoded parameters are

{
  "foo":["bar","baz"]
}

I can't find anything in the documentation for Django or DRF pointing to how the framework can handle this so that I get a QueryDict with the json objects in it by doing something like:

request.query_params # Should yield a dict -> {foo=[bar,baz]}

How can I decode JSON encoded URLs in Django Rest Framework?

Note that my actual parameters are much more complex. Using POST is not an because the caller relies heavily on caching and bookmarking

ritratt
  • 1,703
  • 4
  • 25
  • 45
  • Why are you sending the data like that? Can you change it? – Daniel Roseman Mar 12 '19 at 18:47
  • The reason for receiving data in that form is because the API receives a large number of input parameters. I cannot change that because I do not own my client's code. Even if I did, this would still be the best way barring POST. – ritratt Mar 12 '19 at 18:51
  • There is no standard for sending structured form parameters in URLs, so `request.query_params` can't just parse a random format that purports to contain structure. All we have is [url-encoded query parameters](https://url.spec.whatwg.org/#application/x-www-form-urlencoded), which contains a series of `name=value` pairs separated by `&`. – Martijn Pieters Mar 12 '19 at 18:52
  • 2
    So what you really should do, is [to prefix the json string with a key name](https://stackoverflow.com/questions/15872658/standardized-way-to-serialize-json-to-query-string), so it can at least be extracted as just another query parameter. And be aware that URLs have a [rather low length limit](https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers). – Martijn Pieters Mar 12 '19 at 18:55

3 Answers3

8

The Django request.GET object, and the request.query_params alias that Django REST adds, can only parse application/x-www-form-urlencoded query strings, the type encoded by using a HTML form. This format can only encode key-value pairs. There is no standard for encoding JSON into a query string, partly because URLs have a rather limited amount of space.

If you must use JSON in a query string, it'd be much easier for you if you prefixed the JSON data with a key name, so you can at least have Django handle the URL percent encoding for you.

E.g.

/endpoint?json=%7B%0D%0A++%22foo%22%3A%5B%22bar%22%2C%22baz%22%5D%0D%0A%7D

can be accessed and decoded with:

import json

json_string = request.query_params['json']
data = json.loads(json_string)

If you can't add the json= prefix, you need to decode the URL percent encoding yourself with urllib.parse.unquote_plus(), from the request.META['QUERY_STRING'] value:

from urllib.parse import unquote_plus
import json

json_string = unquote_plus(request.META['QUERY_STRING'])
data = json.loads(json_string)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1

urllib ought to do it:

from urllib.parse import unquote
url = "endpoint?%7B%0D%0A++%22foo%22%3A%5B%22bar%22%2C%22baz%22%5D%0D%0A%7D"
url = unquote(url)
print(url)

The above almost works, but the encoding might be incorrect, not sure:

endpoint?{
++"foo":["bar","baz"]
}
Paolo
  • 20,112
  • 21
  • 72
  • 113
Richard Dunn
  • 6,165
  • 1
  • 25
  • 36
0
def getParams(request):
    url_string = request.META["QUERY_STRING"]
    params = {}
    url_params = url_string.split("&", 1)
    for url_param in url_params:
        print(url_param)
        url_param_values = url_param.split("=", 1)
        if len(url_param_values) == 2:
            params[url_param_values[0]] = url_param_values[1]
    return params

I created this function to get a query parameter that looks like ref=Y3e8rowMetWiBA40f2HtBGsRfb76IOXykAMxcNcog7w=&vef=qDMdvZb+P9sNWbH/dYrDztq79SV6wQ+vf9z1qlUzf3U=

Walulya francis
  • 353
  • 2
  • 10