Retrofitでカスタムアノテーションを使う

Retrofit 2.5.0からカスタムアノテーションが使えるようになったので、それの紹介です。

例をあげて説明します。特定のリクエストのヘッダーに認証情報を付与したいとします。

まず最初にアノテーションを定義します。

annotation class RequireAuth

次に、上記で定義したアノテーションを使い、Apiを定義します。

interface ApiService {
  @RequireAuth
  @GET("login")
  fun login(: retrofit2.Call<Unit>
}

次に、RequireAuthを処理するためのokhttp3.Interceptorを定義します。

class AuthInterceptor : Interceptor {
  override fun intercept(chain: Interceptor.Chain): Response {
    var request = chain.request()

    val invocation = request.tag(Invocation::class.java)
    val authAnnotation = invocation?.method()?.getAnnotation(RequireAuth::class.java)
    if (authAnnotation != null) {
      request = request
        .newBuilder()
        .addHeader("Authorization", "Basic AAAAA").build()
    }
    return chain.proceed(request)
  }
}

ここでのポイントは、val invocation = request.tag(Invocation::class.java)です。 Retrofit 2.5.0からInvocationが追加され、RequestからInvocationが取得できるようになりました。 Invocationには、処理しているRequestjava.lang.reflect.Methodが格納されており、 そこからアノテーションの情報を取得することができます。

val authAnnotation = invocation?.method()?.getAnnotation(RequireAuth::class.java)で、 メソッドにRequireAuthアノテーションが付与されているかどうかを知ることが出来ます。 RequireAuthアノテーションがついていれば、Requestのヘッダーに認証情報を追加します。

最後に、上記のInterceptorをOkHttpクライアントに付与します。

val client = OkHttpClient.Builder()
    .addInterceptor(AuthInterceptor())
    .build()

これでRetrofitでカスタムアノテーションを使うことが出来ます!!

Invocation以前の場合

Invocation以前は、Headersを使い認証情報を付与するテクニックがありました。 詳しくはMaking Retrofit Work For Youにあります。

@Headers("Auth: true")
@GET("useheaderlogin")
fun login(): retrofit2.Call<Unit>
class Auth2Interceptor : Interceptor {
  override fun intercept(chain: Interceptor.Chain): Response {
    var request = chain.request()

    if (request.header("Auth") != null) {
      request = request
        .newBuilder()
        .addHeader("Authorization", "Basic BBBBB").build()
    }
    return chain.proceed(request)
  }
}

このコードでも動くのですが、カスタムアノテーションを定義するやり方のほうが意味が伝わりやすいと思うので、よりよいと思います。

まとめ

今回の検証に用いたサンプルコードはここにあります😃

Written by