在OkHTTP中添加攔截器

1.概述

通常,在我們的Web應用程序中管理HTTP請求的請求和響應週期時,我們需要一種方法來利用此鏈。通常情況下,這是我們履行我們的要求,或我們的servlet代碼完成後,只需之前添加一些自定義的行為。

OkHttp是適用於Android和Java應用程序的高效HTTP和HTTP / 2客戶端。在以前的教程中,我們研究瞭如何使用OkHttp的基礎知識。

在本教程中,我們將學習有關如何攔截HTTP請求和響應對象的所有信息

2.攔截器

顧名思義,攔截器是可插入的Java組件,在將請求發送到我們的應用程序代碼之前,我們可以使用它們來攔截和處理請求。

同樣,它們為我們提供了一種強大的機制,可讓我們在容器將響應發送回客戶端之前處理服務器響應。

當我們想要更改HTTP請求中的某些內容(例如添加新的控件標頭,更改請求的正文或僅生成日誌以幫助我們進行調試)時,此功能特別有用。

使用攔截器的另一個不錯的功能是,它們使我們可以將通用功能封裝在一個地方。假設我們要在全局上對所有請求和響應對象應用某種邏輯,例如錯誤處理。

將這種邏輯放入攔截器至少有兩個優點:

  • 我們只需要將此代碼維護在一個地方,而不是所有端點
  • 發出的每個請求都以相同的方式處理錯誤

最後,我們還可以監視,重寫和重試來自攔截器的調用。

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);

 }

 }

如我們所見,要創建攔截器,我們需要做的就是從Interceptor接口繼承,該接口具有一個強制方法intercept(Chain chain)然後,我們可以繼續使用自己的實現重寫此方法。

首先,在打印出標題和請求URL之前,我們通過調用chain.request()

重要的是要注意,每個攔截器實現的關鍵部分是對chain.proceed(request)的調用。

這種簡單的方法是我們發出信號要打入我們的應用程序代碼,並生成響應來滿足請求的地方。

5.1合併一起使用

要真正利用此攔截器,我們要做的就是在構建OkHttpClient addInterceptor方法,它應該可以正常工作:

OkHttpClient client = new OkHttpClient.Builder()

 .addInterceptor(new SimpleLoggingInterceptor())

 .build();

我們可以繼續為需要的addInterceptor數量的攔截器調用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規則。

這是一個輕量級的,可編寫腳本的Web服務器,用於測試HTTP客戶端,我們將使用它們來測試攔截器。通過使用此規則,我們將為每個集成測試創建服務器的干淨實例。

考慮到這一點,現在讓我們來看一下測試的關鍵部分:

  • 首先,我們設置了一個模擬響應,該響應在主體中包含一條簡單消息
  • 然後,我們構建OkHttpClient並配置SimpleLoggingInterceptor
  • 接下來,我們使用一個User-Agent標頭設置要發送的請求
  • 最後一步是發送請求並驗證響應代碼和收到的正文是否符合預期

5.3。運行測試

最後,當我們運行測試時,我們將看到記錄的HTTP User-Agent標頭:

16:07:02.644 [main] INFO cboiSimpleLoggingInterceptor - 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響應頭。

如果我們要添加自己的專有應用程序HTTP標頭或重寫從服務器返回的標頭之一,則這可能很有用

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方法,但是這次沒有預先使用request對象。當響應返回時,我們使用它來創建新響應並將Cache-Control標頭設置為no-store

實際上,我們不太希望每次都告訴瀏覽器從服務器中拉出,但是我們可以使用這種方法在響應中設置任何標頭。

7.使用攔截器進行錯誤處理

如前所述,我們還可以使用攔截器來封裝一些我們想全局應用於所有請求和響應對象的邏輯,例如錯誤處理。

假設我們想在響應不是HTTP 200響應時返回帶有狀態和消息的輕量級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);



 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響應,應用程序攔截器也總是被調用一次
  • 網絡攔截器掛接到網絡級別,是放置重試邏輯的理想位置
  • 同樣,當我們的邏輯不依賴於響應的實際內容時,我們應該考慮使用網絡攔截器
  • 使用網絡攔截器可以使我們訪問承載請求的連接,包括用於連接到Web服務器的IP地址和TLS配置之類的信息。
  • 應用程序攔截器無需擔心中間響應,例如重定向和重試
  • 相反,如果我們有適當的重定向,則可能會多次調用網絡攔截器

如我們所見,這兩種選擇都有各自的優點。因此,這實際上取決於我們將為之選擇的特定用例。

但是,應用程序攔截器通常會很好地完成工作。

9.結論

在本文中,我們學習了有關如何使用OkHttp創建攔截器的所有信息。首先,我們首先說明什麼是攔截器,以及如何定義一個簡單的日誌記錄攔截器來檢查我們的HTTP請求標頭。

然後,我們了解瞭如何在響應對像中設置標頭以及其他主體。最後,我們快速了解了應用程序攔截器和網絡攔截器之間的一些區別。