OkHTTPにインターセプターを追加する
1. 概要
通常 WebアプリケーションでHTTPリクエストのリクエストとレスポンスのサイクルを管理する場合、このチェーンを利用する方法が必要になります。 通常、これは に リクエストを満たす前にカスタム動作を追加するか、 単に サーブレットコードが完了した後.
OkHttp は、AndroidおよびJavaアプリケーション用の効率的なHTTPおよびHTTP/2クライアントです。 前のチュートリアルでは、OkHttpの操作方法の基本について説明しました。
このチュートリアルでは、HTTPリクエストおよびレスポンスオブジェクトをインターセプトする方法についてすべて学習します。
2. インターセプター
名前が示すように、インターセプターはプラグイン可能なJavaコンポーネントであり、アプリケーションコードに送信される前にリクエストをインターセプトして処理するために使用できます。
同様に、コンテナが応答をクライアントに送り返す前に、サーバーの応答を処理するための強力なメカニズムを提供します。
これは、新しいコントロールヘッダーの追加、リクエストの本文の変更、デバッグに役立つログの作成など、HTTPリクエストの内容を変更する場合に特に便利です。
インターセプターを使用するもう1つの優れた機能は、共通の機能を1か所にカプセル化できることです。 エラー処理など、すべての要求オブジェクトと応答オブジェクトにグローバルにロジックを適用したいとします。
この種のロジックをインターセプターに入れることには、少なくとも2つの利点があります。
- すべてのエンドポイントではなく、このコードを1か所で維持するだけで済みます
- 行われたすべてのリクエストは、同じ方法でエラーを処理します
最後に、インターセプターからの呼び出しを監視、書き換え、再試行することもできます。
3. 一般的な使用法
インセプタが明白な選択である可能性がある場合のその他の一般的なタスクには、次のものがあります。
- リクエストパラメータとその他の有用な情報のロギング
- リクエストに認証と承認のヘッダーを追加する
- リクエストとレスポンスの本文のフォーマット
- クライアントに送信された応答データの圧縮
- いくつかのCookieまたは追加のヘッダー情報を追加して応答ヘッダーを変更する
次のセクションで、これらの実際の例をいくつか見ていきます。
4. 依存関係
もちろん、標準のokhttp依存関係をpom.xmlに追加する必要があります。
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
また、テスト専用の別の依存関係も必要になります。 OkHttp mockwebserverアーティファクトを追加しましょう:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.9.1</version>
<scope>test</scope>
</dependency>
必要な依存関係がすべて構成されたので、先に進んで最初のインターセプターを作成できます。
5. 単純なロギングインターセプターの定義
独自のインターセプターを定義することから始めましょう。 物事を本当に単純にするために、インターセプターはリクエストヘッダーとリクエストURLをログに記録します。
public class SimpleLoggingInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleLoggingInterceptor.class);
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
LOGGER.info("Intercepted headers: {} from URL: {}", request.headers(), request.url());
return chain.proceed(request);
}
}
ご覧のとおり、インターセプターを作成するには、1つの必須メソッドintercept(チェーンチェーン)を持つInterceptorインターフェイスから継承するだけです。次に、このメソッドを独自のメソッドでオーバーライドできます。実装。
まず、ヘッダーとリクエストURLを出力する前に、 chain.request()と呼ばれる着信リクエストを取得します。
すべてのインターセプターの実装の重要な部分は、 chain.proceed(request)の呼び出しであることに注意することが重要です。
この見た目のシンプルな方法では、アプリケーションコードをヒットすることを通知し、リクエストを満たすための応答を生成します。
5.1. 一緒に差し込む
このインターセプターを実際に使用するには、OkHttpClientインスタンスをビルドするときにaddInterceptorメソッドを呼び出すだけで、次のように機能するはずです。
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SimpleLoggingInterceptor())
.build();
必要な数のインターセプターに対して、addInterceptorメソッドを引き続き呼び出すことができます。 追加した順序で呼び出されることを覚えておいてください。
5.2. インターセプターのテスト
これで、最初のインターセプターを定義しました。 先に進んで、最初の統合テストを書いてみましょう。
@Rule
public MockWebServer server = new MockWebServer();
@Test
public void givenSimpleLogginInterceptor_whenRequestSent_thenHeadersLogged() throws IOException {
server.enqueue(new MockResponse().setBody("Hello Baeldung Readers!"));
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SimpleLoggingInterceptor())
.build();
Request request = new Request.Builder()
.url(server.url("/greeting"))
.header("User-Agent", "A Baeldung Reader")
.build();
try (Response response = client.newCall(request).execute()) {
assertEquals("Response code should be: ", 200, response.code());
assertEquals("Body should be: ", "Hello Baeldung Readers!", response.body().string());
}
}
まず、OkHttp MockWebServer JUnitルールを使用しています。
これは、インターセプターのテストに使用するHTTPクライアントをテストするための軽量でスクリプト可能なWebサーバーです。 このルールを使用して、統合テストごとにサーバーのクリーンなインスタンスを作成します。
それを念頭に置いて、テストの重要な部分を見ていきましょう。
- まず、本文に簡単なメッセージを含む模擬応答を設定します
- 次に、 OkHttpClient を構築し、SimpleLoggingInterceptorを構成します。
- 次に、1つのUser-Agentヘッダーを使用して送信するリクエストを設定します
- 最後のステップは、リクエストを送信し、受信した応答コードと本文が期待どおりであることを確認することです
5.3. テストの実行
最後に、テストを実行すると、HTTP User-Agentヘッダーがログに記録されます。
16:07:02.644 [main] INFO c.b.o.i.SimpleLoggingInterceptor - Intercepted headers: User-Agent: A Baeldung Reader
from URL: http://localhost:54769/greeting
5.4. 組み込みのHttpLoggingInterceptorを使用する
ロギングインターセプターはインターセプターを定義する方法をよく示していますが、OkHttpには利用できる組み込みのロガーがあることを言及する価値があります。
このロガーを使用するには、追加のMaven依存関係が必要です。
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>4.9.1</version>
</dependency>
次に、先に進んでロガーをインスタンス化し、関心のあるロギングレベルを定義できます。
HttpLoggingInterceptor logger = new HttpLoggingInterceptor();
logger.setLevel(HttpLoggingInterceptor.Level.HEADERS);
この例では、ヘッダーのみを表示することに関心があります。
6. カスタム応答ヘッダーの追加
これで、インターセプターの作成の背後にある基本を理解できました。 ここで、HTTP応答ヘッダーの1つを変更する別の典型的なユースケースを見てみましょう。
これは、独自のアプリケーションHTTPヘッダーを追加したり、サーバーから返されるヘッダーの1つを書き換えたりする場合に役立ちます:
public class CacheControlResponeInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
return response.newBuilder()
.header("Cache-Control", "no-store")
.build();
}
}
以前と同様に、 chain.proceed メソッドを呼び出しますが、今回は事前にリクエストオブジェクトを使用しません。 応答が戻ってきたら、それを使用して新しい応答を作成し、Cache-Controlヘッダーをno-storeに設定します。
実際には、毎回サーバーからプルするようにブラウザーに指示することはほとんどありませんが、このアプローチを使用して、応答に任意のヘッダーを設定できます。
7. インターセプターを使用したエラー処理
前述のように、インターセプターを使用して、エラー処理など、すべての要求オブジェクトと応答オブジェクトにグローバルに適用するロジックをカプセル化することもできます。
応答がHTTP200応答ではない場合に、ステータスとメッセージを含む軽量のJSON応答を返したいと想像してみてください。
それを念頭に置いて、エラーメッセージとステータスコードを保持するための単純なbeanを定義することから始めます。
public class ErrorMessage {
private final int status;
private final String detail;
public ErrorMessage(int status, String detail) {
this.status = status;
this.detail = detail;
}
// Getters and setters
}
次に、インターセプターを作成します。
public class ErrorResponseInterceptor implements Interceptor {
public static final MediaType APPLICATION_JSON = MediaType.get("application/json; charset=utf-8");
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful()) {
Gson gson = new Gson();
String body = gson.toJson(
new ErrorMessage(response.code(), "The response from the server was not OK"));
ResponseBody responseBody = ResponseBody.create(body, APPLICATION_JSON);
ResponseBody originalBody = response.body();
if (originalBody != null) {
originalBody.close();
}
return response.newBuilder().body(responseBody).build();
}
return response;
}
}
簡単に言うと、インターセプターは応答が成功したかどうかを確認し、成功しなかった場合は、応答コードと単純なメッセージを含むJSON応答を作成します。 この場合、元の応答の本文を閉じて、それに関連付けられているリソースを解放することを忘れないでください。
{
"status": 500,
"detail": "The response from the server was not OK"
}
8. ネットワークインターセプター
これまでカバーしてきたインターセプターは、OkHttpがアプリケーションインターセプターと呼んでいるものです。 ただし、OkHttpは、ネットワークインターセプターと呼ばれる別のタイプのインターセプターもサポートしています。
前に説明したのとまったく同じ方法でネットワークインターセプターを定義できます。 ただし、HTTPクライアントインスタンスを作成するときにaddNetworkInterceptorメソッドを呼び出す必要があります:
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new SimpleLoggingInterceptor())
.build();
アプリケーションとネットワークインセプタの重要な違いは次のとおりです。
- HTTP応答がキャッシュから提供される場合でも、アプリケーションインターセプターは常に1回呼び出されます
- ネットワークインターセプターはネットワークレベルに接続し、再試行ロジックを配置するのに理想的な場所です
- 同様に、ロジックが応答の実際のコンテンツに依存しない場合は、ネットワークインターセプターの使用を検討する必要があります
- ネットワークインターセプターを使用すると、Webサーバーへの接続に使用されたIPアドレスやTLS構成などの情報を含め、要求を伝送する接続にアクセスできます。
- アプリケーションインターセプターは、リダイレクトや再試行などの中間応答について心配する必要はありません。
- 逆に、リダイレクトが設定されている場合、ネットワークインターセプターが複数回呼び出される可能性があります
ご覧のとおり、どちらのオプションにも独自のメリットがあります。 ですから、それは私たちが選択する私たち自身の特定のユースケースに本当に依存します。
ただし、多くの場合、アプリケーションインターセプターは問題なく機能します。
9. 結論
この記事では、OkHttpを使用してインターセプターを作成する方法についてすべて学びました。 まず、インターセプターとは何か、HTTPリクエストヘッダーを検査するための単純なロギングインターセプターを定義する方法を説明することから始めました。
次に、応答オブジェクトにヘッダーと別の本文を設定する方法を確認しました。 最後に、アプリケーションインターセプターとネットワークインターセプターの違いについて簡単に説明しました。
いつものように、記事の完全なソースコードは、GitHubでから入手できます。