Рубрики
Без рубрики

Добавление перехватчиков в OHTTP

Автор оригинала: Jonathan Cook.

1. Обзор

Обычно при управлении циклом запросов и ответов HTTP-запросов в наших веб-приложениях нам понадобится способ подключиться к этой цепочке . Обычно это делается для добавления некоторого пользовательского поведения до выполнения наших запросов или просто после завершения кода нашего сервлета .

OkHttp – это эффективный клиент HTTP & HTTP/2 для приложений Android и Java. В предыдущем уроке мы рассмотрели основы работы с OkHttp.

В этом уроке мы узнаем все о том, как мы можем перехватывать наши объекты HTTP-запросов и ответов .

2. Перехватчики

Как следует из названия, перехватчики-это подключаемые компоненты Java, которые мы можем использовать для перехвата и обработки запросов до их отправки в код нашего приложения.

Кроме того, они предоставляют нам мощный механизм для обработки ответа сервера до того, как контейнер отправит ответ обратно клиенту.

Это особенно полезно, когда мы хотим что-то изменить в HTTP-запросе, например, добавить новый заголовок элемента управления, изменить тело нашего запроса или просто создать журналы, которые помогут нам отладить.

Еще одна приятная особенность использования перехватчиков заключается в том, что они позволяют нам инкапсулировать общие функциональные возможности в одном месте. Давайте представим, что мы хотим применить некоторую логику глобально ко всем нашим объектам запросов и ответов, таким как обработка ошибок.

Есть, по крайней мере, несколько преимуществ использования такого рода логики в перехватчике:

  • Нам нужно только поддерживать этот код в одном месте, а не во всех наших конечных точках
  • Каждый сделанный запрос имеет дело с ошибкой таким же образом

Наконец, мы также можем отслеживать, переписывать и повторять вызовы от наших перехватчиков.

3. Общее использование

Некоторые другие распространенные задачи, когда инициатор может быть очевидным выбором, включают:

  • Регистрация параметров запроса и другой полезной информации
  • Добавление заголовков аутентификации и авторизации в наши запросы
  • Форматирование наших органов запросов и ответов
  • Сжатие данных ответа, отправленных клиенту
  • Изменение заголовков наших ответов путем добавления некоторых файлов cookie или дополнительной информации о заголовках

Мы увидим несколько примеров этого в действии в следующих разделах.

4. Зависимости

Конечно, нам нужно будет добавить стандартную okhttp зависимость в ваш pom.xml :


    com.squareup.okhttp3
    okhttp
    4.9.1

Нам также понадобится еще одна зависимость специально для наших тестов. Давайте добавим OkHttp mockwebserver артефакт :


    com.squareup.okhttp3
    mockwebserver
    4.9.1
    test

Теперь, когда у нас настроены все необходимые зависимости, мы можем продолжить и написать наш первый перехватчик.

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) . Тогда мы можем пойти дальше и переопределить этот метод с помощью нашей собственной реализации.

Во-первых, мы получаем входящий запрос, вызвав chain.request() перед распечаткой заголовков и URL-адреса запроса.

Важно отметить, что важной частью реализации каждого перехватчика является вызов цепочка.продолжить(запрос) .

Этот простой на вид метод заключается в том, что мы сигнализируем, что хотим попасть в код нашего приложения, создавая ответ для удовлетворения запроса.

5.1. Подключение Его Вместе

Чтобы на самом деле использовать этот перехватчик, все, что нам нужно сделать, это вызвать метод addInterceptor при создании нашего экземпляра OkHttpClient , и он должен просто работать:

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-клиентов, который мы собираемся использовать для тестирования наших перехватчиков . Используя это правило, мы создадим чистый экземпляр сервера для каждого интеграционного теста.

Имея это в виду, давайте теперь пройдемся по ключевым частям нашего теста:

  • Прежде всего, мы создаем макет ответа, который содержит простое сообщение в теле
  • Затем мы создаем наш OkHttpClient и настраиваем наш Простой перехватчик ведения журнала
  • Затем мы настроим запрос, который мы собираемся отправить, с одним заголовком 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

Хотя наш logginginterceptor хорошо демонстрирует, как мы можем определить перехватчик, стоит отметить, что в OkHttp есть встроенный регистратор, которым мы можем воспользоваться.

Чтобы использовать этот регистратор, нам нужна дополнительная зависимость Maven :


    com.squareup.okhttp3
    logging-interceptor
    4.9.1

Затем мы можем создать экземпляр нашего регистратора и определить уровень ведения журнала, который нас интересует:

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 , но на этот раз без предварительного использования объекта запроса. Когда ответ возвращается, мы используем его для создания нового ответа и устанавливаем заголовок Cache-Control в no-store .

На самом деле, маловероятно, что мы захотим каждый раз указывать браузеру, чтобы он извлекал данные с сервера, но мы можем использовать этот подход для установки любого заголовка в нашем ответе.

7. Обработка Ошибок С Использованием Перехватчиков

Как упоминалось ранее, мы также можем использовать перехватчики для инкапсуляции некоторой логики, которую мы хотим применить глобально ко всем нашим объектам запросов и ответов, таким как обработка ошибок.

Давайте представим, что мы хотим вернуть легкий ответ JSON со статусом и сообщением, когда ответ не является ответом HTTP 200.

Имея это в виду, мы начнем с определения простого компонента для хранения сообщения об ошибке и кода состояния:

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 называет перехватчиками приложений. Однако OHTTP также поддерживает другой тип перехватчиков, называемый сетевыми перехватчиками.

Мы можем определить наши сетевые перехватчики точно так же, как объяснялось ранее. Однако нам нужно будет вызвать метод add Network Interceptor при создании экземпляра HttpClient :

OkHttpClient client = new OkHttpClient.Builder()
  .addNetworkInterceptor(new SimpleLoggingInterceptor())
  .build();

Некоторые из важных различий между приложениями и сетевыми инициаторами включают:

  • Перехватчики приложений всегда вызываются один раз, даже если HTTP-ответ подается из кэша
  • Сетевой перехватчик подключается к сетевому уровню и является идеальным местом для размещения логики повторных попыток
  • Аналогичным образом, мы должны рассмотреть возможность использования сетевого перехватчика, когда наша логика на самом деле не зависит от фактического содержания ответа
  • Использование сетевого перехватчика дает нам доступ к соединению, которое несет запрос, включая такую информацию, как IP-адрес и конфигурация TLS, которая использовалась для подключения к веб-серверу.
  • Перехватчикам приложений не нужно беспокоиться о промежуточных ответах, таких как перенаправление и повторные попытки
  • Напротив, сетевой перехватчик может быть вызван несколько раз, если у нас есть перенаправление

Как мы видим, оба варианта имеют свои достоинства. Так что это действительно зависит от вашего конкретного варианта использования, для которого мы выберем.

Однако чаще всего перехватчики приложений прекрасно справляются с этой задачей.

9. Заключение

В этой статье мы узнали все о том, как создавать перехватчики с помощью OHTTP. Во-первых, мы начали с объяснения того, что такое перехватчик и как мы можем определить простой перехватчик ведения журнала для проверки заголовков наших HTTP-запросов.

Затем мы увидели, как установить заголовок, а также другое тело в наши объекты ответа. Наконец, мы быстро рассмотрели некоторые различия между перехватчиками приложений и сетевыми перехватчиками.

Как всегда, полный исходный код статьи доступен на GitHub .