RetrofitのAPI定義interfaceから通信せずにURLだけ取り出す

# はじめに

メソッドの返り値を`Call<Void>`にすればリクエスト取り出せますね。
CallAdapterFactoryを追加したら普通の`Call<T>`で返せないかと思ってました。

# まちがい

例えばこんなinterfaceがあります。

```SuzuriAPI.java
public interface SuzuriAPI {

    @GET("/api/v1/activities")
    Single<Activities> activities();

    @GET("/api/v1/activities/unreads")
    Single<Unreads> activitiesUnreads();
}

```

これはGMOペパボが運営しているSUZURIというサービスのAPIを利用するために書いたものです。
「自分だけのオリジナルグッズが作れる。売れる。買える。」というやつです。
https://suzuri.jp/
https://suzuri.jp/developer
https://twitter.com/suzurijp

こんな風にして使います。

```
        OkHttpClient client;
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://suzuri.jp/")
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        api = retrofit.create(SuzuriAPI.class);
        api.activities().subscribe(new Consumer<Activities>() {
            @Override
            public void accept(Activities activities) throws Exception {
                // レスポンスのjsonがGsonでPOJOに変換されたものをRxのSingle経由で受け取ります。
                // なお、このActivitiesはAndroidのActivityとは無関係です。
            }
        });
```

実際には、APIを利用する前に、ユーザに認可してもらって、アクセストークンを取得する必要があります。
一部だけ自分でURLを組み立てるのも微妙ですし、Retrofit用のinterfaceは視認性が良いので、
このOAuth部分（認可とアクセストークン取得）についてもAPIの定義と同じように書いておきたいですね。
（APIとは別ファイルに書くべきかもしれませんが、ここではまとめます。）

アクセストークンの取得はAPI部分と同じように書いて、Retrofit経由で通信することができます。

```SuzuriAPI.java
public interface SuzuriAPI {

    // アクセストークンの取得もRetrofitで定義
    @FormUrlEncoded
    @POST("/oauth/token")
    Single<AccessToken> accessToken(@Field("grant_type") String grant_type,
                                    @Field("code") String code,
                                    @Field("redirect_uri") String redirect_uri,
                                    @Field("client_id") String client_id,
                                    @Field("client_secret") String client_secret);
}
```

ところが、OAuthでの認可画面はAndroidだとChromeなどの外部ブラウザで表示します。
つまり、Retrofit経由では通信しません。
そして、Retrofitでは実際のリクエストが隠蔽されています。
そのため、interfaceに定義しておいても組み立てられたURLを取り出すことができません。

```SuzuriAPI.java
public interface SuzuriAPI {

    // 認可画面はブラウザで行うため、Retrofitでは通信しない
    @GET("/oauth/authorize")
    Single<?> authorize(@Query("client_id") String client_id,
                        @Query("scope") String scope,
                        @Query("redirect_uri") String redirect_uri,
                        @Query("response_type") String response_type);
}
```

そこで、Retrofitが実際の通信に使っているOkHttpClientに、Interceptorを追加します。
インターセプトの条件については、ちょっと邪悪ですが、ヘッダーに目印を付けます。

```SuzuriAPI.java
    @Headers("X-Dummy: Dummy")
    @GET("/oauth/authorize")
    Single<Dummy> authorize(
```

Interceptorでそのヘッダーを見つけたら、ダミーのレスポンスを作って返します。

```
        OkHttpClient client = defaultClient.newBuilder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(@NonNull Chain chain) throws IOException {
                        final Request original = chain.request();
                        if (original.header("X-Dummy") != null) {
                            Dummy dummy = new Dummy(original.url().toString());
                            ResponseBody body = ResponseBody.create(null, gson.toJson(dummy));
                            return new Response.Builder()
                                    .request(original)
                                    .protocol(Protocol.HTTP_1_0)
                                    .code(200)
                                    .message("OK")
                                    .body(body)
                                    .build();
                        }
                        return chain.proceed(original);
                    }
                })
                .build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://suzuri.jp/")
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
```

```Dummy.java
public class Dummy {
    public final String value;

    public Dummy(String value) {
        this.value = value;
    }
}
```

Responseの`request`と`protocol`と`code`と`message`は必須なので、適当な値を設定しています。
以下のようにして取り出します。

```
        api.authorize(CLIENT_ID, SCOPE, REDIRECT_URI, "code")
                .subscribe(new Consumer<Dummy>() {
                    @Override
                    public void accept(Dummy dummy) throws Exception {
                        // dummy.valueが組み立てられたURL
                    }
                });
```

~~（Retrofitにまともに触る初日なので単に見落としてるだけかもしれません……）~~

# おわりに

冒頭で述べたように`Call<Void>`を返すメソッドを定義しましょう