LoginSignup
2
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-10-22

はじめに

メソッドの返り値を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のrequestprotocolcodemessageは必須なので、適当な値を設定しています。
以下のようにして取り出します。

        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>を返すメソッドを定義しましょう

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2