I'm using Retrofit for my network layer in my Android app, but I have a problem with the URL encoding.
I have to call an REST API like that:
https://my_hostname.com/some_path?q=some_query¶m[0]=value1¶m[1]=value2&other_param=abcd
as you can see the query string is composed by some different kind of parameters, so I decided to use the @QueryMap
annotation in the Retrofit Interface with a Map<String, String>
where
q, param[1], param[0], other_param
are String keys of the map
What do I expect?
I expect the square brackets in the URL are encoded with %5B
for '['
and %5D
for '['
, but this does not happen.
Why does this happen? The square brackets should be encoded with percent encoding. Is this a bug or I'm doing something wrong? I also tried the @EncodedQueryMap
annotation with no differences.
A host identified by an Internet Protocol literal address, version 6 [RFC3513] or later, is distinguished by enclosing the IP literal within square brackets ("[" and "]"). This is the only place where square bracket characters are allowed in the URI syntax.
encode() method is used to produce a URL query string from the given object that contains the key-value pairs. The method iterates through the object's own properties to generate the query string. It can serialize a single or an array of strings, numbers, and boolean.
Adding query parameters to single requests is straight forward. You're using the @Query annotation for your parameter declaration within the interface methods. This tells Retrofit to translate the provided query parameter name and value to the request and append the fields to the url.
Why do we need to encode? URLs can only have certain characters from the standard 128 character ASCII set. Reserved characters that do not belong to this set must be encoded. This means that we need to encode these characters when passing them into a URL.
Query names are never URL encoded.
The documentation for @QueryMap
states:
Values are URL encoded.
And for @EncodedQueryMap
:
Values are not URL encoded.
However, I just submitted a pull request to change this behavior a bit. I am adding support for encoding keys by using @Query(value = "..", encodeName = true)
or @QueryMap(encodeNames = true)
.
I just had to deal with @QueryMap
encoding everything but {}[]
. Most of the time I don't mind because I rarely send json in the query, and want to push the encoding down as low as possible in my application's stack, but recently I had to add an endpoint just like Ivan describes. My solution was to add an interceptor that does this:
Request originalRequest = chain.request();
HttpUrl url = originalRequest.url();
String urlFilePath = url.encodedPath();
if(url.encodedQuery() != null) {
// Because Retrofit doesn't think "{}[]" should be encoded
// but our API does
String correctlyEncodedQuery = correctlyEncodeQuery(url);
urlFilePath += "?" + correctlyEncodedQuery;
}
URL correctUrl = new URL(url.scheme(), url.host(), url.port(),
urlFilePath);
Request newRequest = originalRequest.newBuilder()
.url(correctUrl)
private String correctlyEncodeQuery(HttpUrl url) throws UnsupportedEncodingException {
String retVal = "";
for(String queryKey : url.queryParameterNames()){
if(retVal.length() > 0){
retVal += "&";
}
String queryValue = url.queryParameter(queryKey);
retVal += queryKey + "=" + URLEncoder.encode(queryValue, "utf-8");
}
return retVal;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With