user2934930
user2934930

Reputation: 1146

How to avoid URL encoding in Retrofit?

I'm using retrofit for my network layer I'm facing a strange issue I've the parameter and it's value look like this

store://0TxIGQMQbObzvU4Apia0V0&callback=

and after encoding it changes to this

store://0TxIGQMQbObzvU4Apia0V0%26callback%3D

and for some reason server is not liking this encoding and I'm getting "HTTP 400 Bad Request".

If I hit without encoding it runs fine so I was wondering is there is any way i can disable the endoing.

I've tried other ways like this

@Query(value "env" encoded = false) String env

but no luck.

This is my network interface class

public interface NetworkService
{
    @GET("v1/public/yql")
    Observable<SeriesEntity> getOnGoingSeries(@Query("q") String query,
                                              @Query("format") String format,
                                              @Query("diagnostics") boolean flag,
                                              @Query("env") String env);


    class Factory {
        public  static NetworkService create()
        {
            OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            clientBuilder.addInterceptor(loggingInterceptor);

            Gson gson = new GsonBuilder().disableHtmlEscaping().create();

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("https://query.yahooapis.com")
                    .client(clientBuilder.build())
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();

            return retrofit.create(NetworkService.class);
        }
    }
}

Edit :

I also notice that the issue is only the encoded value of '&' because if i replace it value (whic is %26) it works fine

Upvotes: 15

Views: 14067

Answers (5)

Tarun Deep Attri
Tarun Deep Attri

Reputation: 8674

My problem was that I had a query param that was getting encoded. The only solution that worked for me was adding an interceptor that decodes the URL as shown below:

import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import java.nio.charset.StandardCharsets
import javax.inject.Singleton

@Singleton
internal object UrlDecoderInterceptor: Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val original: Request = chain.request()
        var url: String = original.url.toString()
        url = java.net.URLDecoder.decode(url, StandardCharsets.UTF_8.name())
        val requestBuilder: Request.Builder = original.newBuilder().url(url)
        val request: Request = requestBuilder.build()
        return chain.proceed(request)
    }
}

You can use this interceptor while creating the okhttp object as shown below:

val interceptClient = okHttpClient.newBuilder()
                .addInterceptor(UrlDecoderInterceptor)
                .build()

And creating a retrofit builder with this okhttp instance:

Retrofit.Builder().client(interceptClient)
                .baseUrl("https://example.net")
                .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())

Works like a charm.

Upvotes: 1

Felipe Franco
Felipe Franco

Reputation: 179

The URL is:

https://api.themoviedb.org/3/discover/movie?api_key={"PUTAPIKEYHERE!!!}&with_genres=28

I was getting:

https://api.themoviedb.org/3/discover/movie?api_key={"PUTAPIKEYHERE!!!}&with_genres%3D=28
fun getClient(codeForActionOrComedy:Int): MovieDbInterfaceToGetMovies {

        val requestToApiInterceptor=Interceptor { chain ->

            val url =chain.request()
                .url()
                .newBuilder()
                    // ALONE_API is just the "PUTAPIKEYHERE!!!" 
                    .addQueryParameter("api_key","PUTAPIKEYHERE!!!")
                    //codeForActionOrComedy is an Int
                    .addQueryParameter("with_genres","$codeForActionOrComedy")
                    .build()

            val request =chain.request()
                .newBuilder()
                .url(url.toString().replace("%3D","="))
                .build()
            return@Interceptor chain.proceed(request)
        }
        val okHttpClient= OkHttpClient.Builder()
            .addInterceptor(requestToApiInterceptor)
            .connectTimeout(60, TimeUnit.SECONDS)
            .build()

        val httpUrl = Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(BASE_URL)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(MovieDbInterfaceToGetMovies::class.java)

        return httpUrl
}

Upvotes: 0

Francis
Francis

Reputation: 247

Please Use encoded = true

@Query(value = "email", encoded = true)

@HTTP(method = "DELETE", path = ApiConstant.DELETE_CONTACT_INFO, hasBody = true)

Observable deleteContactInfo( @Query(value = "email", encoded = true) String email);

Upvotes: 22

Pritesh Vishwakarma
Pritesh Vishwakarma

Reputation: 348

better solution from Github issue 1199 , we can have custom interceptor is one way to solve this issue. and this solution work for me.

from your case i want to replace %26 with = in url.

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        String string = request.url().toString();
        string = string.replace("%26", "=");
        Request newRequest = new Request.Builder()
                .url(string)
                .build();
        return chain.proceed(newRequest);
    }
});

Upvotes: 3

John O&#39;Reilly
John O&#39;Reilly

Reputation: 10350

the issue I believe is that you're including callback param as part of env one. Try adding @Query("callback") boolean callback to your retrofit interface (and using just store://0TxIGQMQbObzvU4Apia0V0 for env)

Upvotes: 2

Related Questions