Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrofit 2 : Custom annotations for custom interceptor

I have custom interceptor for authentication:

@Named("authInterceptor")
    @Provides
    @Singleton
    fun providesAuthInterceptor(preferencesManager: PreferencesManager): Interceptor {
        return Interceptor { chain ->
            val newBuilder = chain.request().newBuilder()
            newBuilder.addHeader("access-token", preferencesManager.getAccessToken())
            val request = newBuilder.build()
            return@Interceptor chain.proceed(request)
        }
    }

But I have some calls that not need auth header.

What I would like to have in my service is:

interface NetService {
    @NEEDAUTH
    @GET("users")
    fun getAllShops(key: String): Single<SomeResponse>

    @FormUrlEncoded
    @POST("users")
    fun register(@Field("nickname") nickname: String): Single<SomeResponse>
}

So, the first call will use authInterceptor, the second one will not use it.

like image 515
Mladen Rakonjac Avatar asked Dec 11 '17 20:12

Mladen Rakonjac


4 Answers

Since the version of Retrofit 2.6.0, you can get the annotations in OkHttp Interceptor using the tag field like this:

response.request.tag(Invocation::class.java)?.method()?.getAnnotation(YourAnnotation::class.java)

Then inside of the interceptor, you can verify if the request is annotated or no.

Retrofit Changelog:

New: @Tag parameter annotation for setting tags on the underlying OkHttp Request object. These can be read in CallAdapters or OkHttp Interceptors for tracing, analytics, varying behavior, and more.

https://github.com/square/retrofit/pull/2899/files

like image 128
Max Cruz Avatar answered Nov 06 '22 11:11

Max Cruz


I have similar requirement, what I found is Annotation can be read in Converter.Factory:requestBodyConverter(), Converter.Factory:responseBodyConverter() and CallAdapter.Factory.get().

I also found two articles as examples for implementation on each way.

  1. Using Converter: Auto Caching with Retrofit

We’ll use the gson converter (GsonConverterFactory) provided by Retrofit and modify it slightly to include a listener in GsonResponseBodyConverter.class which handles the http response parsing.

In GsonCacheableConverter, it overrides responseBodyConverter() to persist response tagged with @Cacheable.

  1. Using CallAdapter: Custom Annotations with Retrofit 2

We read the annotation in the CallAdapter.Factory and when the request gets created in the CallAdapter, we will store some information for this kind of request within some map, to identify it later in some interceptor.

It uses a custom CallAdapter to get annotation @Authenticated, and put data into a map, which later parsed in the Interceptor.

I think requestBodyConverter() and CallAdapter are closer to your requirement.

While if you do not insist on custom annotations, the easiest way for now in my opinion is to add custom header to the api interface, then read and remove it in the interceptor.

That is, adding @Headers("needauth: 1") to your services, and using chain.request().header("needauth") to get the value.

Example: Sneaking Data into an OkHttp Interceptor.

like image 4
J.Zhang Avatar answered Nov 06 '22 10:11

J.Zhang


Interceptors are concepts that exist in OkHttp, Retrofit knows nothing about them.

What you need to do is have two OkHttp clients, with their respective instances of Retrofit.

  • One with the authentications headers
  • One for the rest

Whether you need the authentication headers or not will decide which instance to inject.

like image 2
elmorabea Avatar answered Nov 06 '22 11:11

elmorabea


Based on Max Cruz answer I'm using as extension function:

fun <T : Annotation> Request.getCustomAnnotation(annotationClass: Class<T>): T? = this.tag(Invocation::class.java)?.method()?.getAnnotation(annotationClass)

And you can use then like that:

request.getCustomAnnotation(YourAnnotation::class.java)
like image 1
zoki Avatar answered Nov 06 '22 11:11

zoki