Home » Android » java – Why Retrofit cannot encode properly query string with square brackets?

java – Why Retrofit cannot encode properly query string with square brackets?

Posted by: admin June 15, 2020 Leave a comment

Questions:

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&param[0]=value1&param[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.

How to&Answers:

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).

Answer:

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;
}

Answer:

Try simple one
@GET
and parameters @Query

Answer:

Moreover, if value contains JSONArray with JSONObject, retrofit doesn’t encode brackets [ ] { }

Map contains element:

key = filter, 
value = [{"field":"some_field","value":"some_value","operator":"="}]

And @QueryMap send

http://my_server/api?filter=[{%22field%22:%22some_field%22,%22value%22:%22some_value%22,%22operator%22:%22%3D%22}]

QueryMap used there because of number params in GET request and objects in filter option.