Retrofit URL query encoding
When you call restful API with Retrofit, sometimes there are query arguments which are JSON objects you should provide.
The json format string contains characters which cannot be used in a URL such as quotation, comma …
Then we have to do encoding on the JSON objects to fit URL format.
First of all, you got an API contract:
private interface SomeApi {
@GET("user/{user_id}/cards")
Single<Response<List<Card>>> getCards(@Path("user_id") String userId, @Query(value = "query", encoded = true) Filter filter);
}
We need to get cards of a user based on a filter. The query argument which marked as @Query would appear in the request URL. But it’s not a builtin type such as int, String …
It’s a Java object which would be turned in to a JSON string then pass to the request URL. So we have to encode it.
public final class EncodingGsonConverterFactory extends Converter.Factory {
public static EncodingGsonConverterFactory create(GsonConverterFactory gsonConverterFactory, Gson gson) {
if (gsonConverterFactory == null)
throw new NullPointerException("gsonConverterFactory == null");
if (gson == null) throw new NullPointerException("gson == null");
return new EncodingGsonConverterFactory(gsonConverterFactory, gson);
}
private final GsonConverterFactory gsonConverterFactory;
private final Gson gson;
private EncodingGsonConverterFactory(GsonConverterFactory gsonConverterFactory, Gson gson) {
this.gsonConverterFactory = gsonConverterFactory;
this.gson = gson;
}
@Nullable
@Override
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return (Converter<Object, String>) value -> {
if (value instanceof CharSequence || value instanceof Boolean || value instanceof Character || value instanceof Number)
return value.toString();
return URLEncoder.encode(gson.toJson(value), "UTF-8");
};
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return gsonConverterFactory.responseBodyConverter(type, annotations, retrofit);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return gsonConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
}
}
Then add the EncodingGsonConverterFactory to your Retrofit instance.
public Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addConverterFactory(EncodingGsonConverterFactory.create(GsonConverterFactory.create(gson), gson))
.build();
}