Spring WebFlux фильтр для перехвата запроса после десериализации объекта запроса - PullRequest
1 голос
/ 05 февраля 2020

У меня есть реактивное приложение WebFlux Spring Boot (версия 2.2.3.RELEASE). Я определил POJO с именем SearchRequest, который используется в запросах GET и POST в аннотированном контроллере:

@GetMapping(path = "/search")
Mono<ItemCollection> getItems(SearchRequest searchRequest);

@PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE)
Mono<ItemCollection> getItemsPost(@RequestBody SearchRequest searchRequest);

Существует некоторая базовая проверка c, которую необходимо выполнить, а также пара возможных преобразований. Некоторые поля в SearchRequest сами являются объектами, например:

public class SearchRequest {

    private Fields fields;
    ...
}

, где класс Fields выглядит следующим образом:

public class Fields {

    private Set<String> include;
    private Set<String> exclude;
    ...
}

Для запросов HTTP POST пользователь может просто отправить JSON объект, который следует этой структуре. Для HTTP-запросов GET спецификация API, с которой я работаю, позволяет вам просто указать URL-параметр fields, который содержит массив имен полей с префиксом «+» или «-». Я создал класс, который расширяет PropertyEditorSupport для анализа параметра URL-адреса и заполнения наборов include и exclude объекта Fields. Это настроено в моем контроллере с использованием @InitBinder:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Fields.class, "fields", fieldsPropertyEditor);
}

Мой SearchRequest объект поддерживает геопространственные запросы, использующие либо значение bbox, либо значение intersects, однако мне нужно вернуть 400, если оба указаны. Я пробовал следующие методы для реализации этой проверки, но не могу найти решение, которое ищу:

  1. Регистрация другого пользовательского редактора с помощью WebDataBinder для класса SearchRequest, который не указать поле.
  2. Регистрация валидатора с помощью WebDataBinder (оба с использованием методов setValidator и addValidators).
  3. Реализация WebFilter.

Решение 1 кажется не иметь никакого эффекта. Редактор никогда не вызывается. В решении 2 метод supports вызывается с каждым запросом, но метод validate никогда не вызывается. Решение 3 может работать, но требует, чтобы я написал два набора logi c - один для GET и один для POST, поскольку параметры URL-адреса GET хранятся отдельно от тела запроса POST. Кроме того, для фактической проверки тела запроса exchange.getRequest.getBody() возвращает DataBuffer, который можно использовать для десериализации тела в объект, но я не могу напрямую десериализовать в SearchRequest без моих ранее упомянутых редакторов свойств ( для запросов GET).

Я действительно надеюсь найти какой-нибудь фильтр / преобразователь / преобразователь, который я смогу вызвать после запроса, десериализованного в объект SearchRequest, но до вызывается метод обработчика контроллера, но я не могу найти способ сделать это.

Может кто-нибудь посоветовать, если это возможно, или единственный вариант - обрабатывать запросы GET / POST отдельно в WebFilter

1 Ответ

0 голосов
/ 06 февраля 2020

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

Аннотация:

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = SingleGeometryValidator.class)
@Documented
public @interface SingleGeometry {
    String message () default "Request should not contain both `intersects` and `bbox`. Please choose one geometry to "
            + "search by.";
    Class<?>[] groups () default {};
    Class<? extends Payload>[] payload () default {};
}

Валидатор:

public class SingleGeometryValidator implements ConstraintValidator<SingleGeometry, SearchRequest> {

    @Override
    public boolean isValid(SearchRequest searchRequest, ConstraintValidatorContext constraintValidatorContext) {
        return !(searchRequest.getBbox() != null && searchRequest.getIntersects() != null);
    }
}

Объект запроса:

@Data
@SingleGeometry
public class SearchRequest {
...
}

Интерфейс контроллера:

@GetMapping(path = "/search")
Mono<ItemCollection> getItems(@Valid SearchRequest searchRequest);

@PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE)
Mono<ItemCollection> getItemsPost(@Valid @RequestBody SearchRequest searchRequest);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...