Другое

Фильтры REST-клиента Quarkus: Полное руководство по регистрации

Узнайте, как автоматически регистрировать ClientRequestFilter и ClientResponseFilter в расширениях Quarkus при миграции с quarkus-resteasy на quarkus-rest. Найдите эквивалентный BuildItem для фильтров на стороне клиента в Quarkus 3.27 и реализуйте автоматическую регистрацию фильтров во всех REST-клиентах.

Как автоматически регистрировать ClientRequestFilter и ClientResponseFilter в расширении Quarkus при миграции с quarkus-resteasy на quarkus-rest? Я успешно использовал ContainerRequestFilterBuildItem и ContainerResponseFilterBuildItem для серверных фильтров, но мне нужен эквивалентный BuildItem для клиентских фильтров в Quarkus 3.27. Ранее с quarkus-resteasy я использовал ResteasyJaxrsProviderBuildItem для автоматической регистрации всех фильтров. Фильтры работают при ручном включении с помощью @RegisterRestClient, но я хочу, чтобы расширение автоматически регистрировало их во всех REST-клиентах.

Разработка расширений Quarkus: ClientRequestFilterBuildItem и ClientResponseFilterBuildItem

При разработке расширений для Quarkus, особенно работающих с Quarkus REST (ранее RESTEasy), часто возникает необходимость работать с фильтрами HTTP-запросов и ответов. В этом контексте два важных BuildItem - ClientRequestFilterBuildItem и ClientResponseFilterBuildItem - играют ключевую роль.

ClientRequestFilterBuildItem

ClientRequestFilterBuildItem используется для регистрации фильтров, которые будут применяться к исходящим HTTP-запросам при использовании Quarkus REST клиентских возможностей.

Основные сценарии использования:

  1. Добавление заголовков ко всем исходящим запросам
  2. Логирование исходящих запросов
  3. Аутентификация и авторизация
  4. Модификация запросов перед отправкой

Пример использования:

java
import io.quarkus.builder.item.BuildItem;
import io.quarkus.rest.client.reactive.ClientRequestFilterBuildItem;

public class MyClientRequestFilterBuildItem implements BuildItem {

    private final Class<?> filterClass;

    public MyClientRequestFilterBuildItem(Class<?> filterClass) {
        this.filterClass = filterClass;
    }

    public Class<?> getFilterClass() {
        return filterClass;
    }
}

Регистрация фильтра:

java
@BuildStep
ClientRequestFilterBuildItem registerClientRequestFilter() {
    return new ClientRequestFilterBuildItem(MyClientRequestFilter.class);
}

ClientResponseFilterBuildItem

ClientResponseFilterBuildItem используется для регистрации фильтров, которые будут обрабатывать входящие HTTP-ответы от REST-клиентов.

Основные сценарии использования:

  1. Обработка ошибок ответа
  2. Логирование входящих ответов
  3. Парсинг и преобразование ответов
  4. Обработка особых статусов ответа

Пример использования:

java
import io.quarkus.builder.item.BuildItem;
import io.quarkus.rest.client.reactive.ClientResponseFilterBuildItem;

public class MyClientResponseFilterBuildItem implements BuildItem {

    private final Class<?> filterClass;

    public MyClientResponseFilterBuildItem(Class<?> filterClass) {
        this.filterClass = filterClass;
    }

    public Class<?> getFilterClass() {
        return filterClass;
    }
}

Регистрация фильтра:

java
@BuildStep
ClientResponseFilterBuildItem registerClientResponseFilter() {
    return new ClientResponseFilterBuildItem(MyClientResponseFilter.class);
}

Полный пример расширения

Вот полный пример расширения, которое регистрирует как фильтр запросов, так и фильтр ответов:

java
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.builder.BuildStep;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.rest.client.reactive.ClientRequestFilterBuildItem;
import io.quarkus.rest.client.reactive.ClientResponseFilterBuildItem;

public class MyRestClientExtension {

    @BuildStep
    AdditionalBeanBuildItem registerMyFilters() {
        return AdditionalBeanBuildItem.builder()
                .addBeanClass(MyClientRequestFilter.class)
                .addBeanClass(MyClientResponseFilter.class)
                .build();
    }

    @BuildStep
    ClientRequestFilterBuildItem registerClientRequestFilter(BuildProducer<ClientRequestFilterBuildItem> producer) {
        return producer.create(MyClientRequestFilter.class);
    }

    @BuildStep
    ClientResponseFilterBuildItem registerClientResponseFilter(BuildProducer<ClientResponseFilterBuildItem> producer) {
        return producer.create(MyClientResponseFilter.class);
    }
}

Примеры фильтров

Фильтр запросов:

java
import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext;
import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestFilter;

import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;

@Priority(Priorities.AUTHENTICATION)
public class MyClientRequestFilter implements ResteasyReactiveClientRequestFilter {

    @Override
    public void filter(ResteasyReactiveClientRequestContext context) {
        // Добавляем кастомный заголовок ко всем запросам
        context.getHeaders().add("X-Request-ID", generateRequestId());
        
        // Логируем исходящий запрос
        System.out.println("Sending request to: " + context.getUri());
    }

    private String generateRequestId() {
        return java.util.UUID.randomUUID().toString();
    }
}

Фильтр ответов:

java
import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientResponseContext;
import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientResponseFilter;

import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.core.Response;

@Priority(Priorities.USER)
public class MyClientResponseFilter implements ResteasyReactiveClientResponseFilter {

    @Override
    public void filter(ResteasyReactiveClientResponseContext context) {
        // Проверяем статус ответа
        if (context.getStatus() >= 400) {
            System.out.println("Error response received: " + context.getStatus());
        }
        
        // Логируем входящий ответ
        System.out.println("Received response with status: " + context.getStatus());
    }
}

Настройка через application.properties

Вы можете настроить поведение фильтров через файл application.properties:

properties
# Включение или отключение фильтров
my.rest.client.request-filter.enabled=true
my.rest.client.response-filter.enabled=true

# Настройка параметров фильтров
my.rest.client.request-header=X-Custom-Header
my.rest.client.log-requests=true

Тестирование расширения

Для тестирования вашего расширения с фильтрами можно использовать интеграционные тесты:

java
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import jakarta.inject.Inject;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.Response;

import static org.junit.jupiter.api.Assertions.assertEquals;

@QuarkusTest
public class MyRestClientExtensionTest {

    @Inject
    Client client;

    @Test
    public void testClientRequestFilter() {
        Response response = client.target("/test-endpoint")
                .request()
                .get();
        
        assertEquals(200, response.getStatus());
        // Дополнительные проверки для фильтров
    }
}

Заключение

ClientRequestFilterBuildItem и ClientResponseFilterBuildItem являются мощными инструментами при разработке расширений Quarkus для работы с REST-клиентами. Они позволяют гибко настраивать обработку как исходящих запросов, так и входящих ответов, что особенно полезно для кросс-забоченностей таких как аутентификация, логирование, обработка ошибок и т.д.

Правильное использование этих BuildItem обеспечивает интеграцию ваших фильтров в экосистему Quarkus REST и позволяет создавать переиспользуемые и конфигурируемые компоненты для ваших приложений.

Авторы
Проверено модерацией
Модерация