Как извлечь тело запроса в реактивную функцию-обработчик Spring? - PullRequest
2 голосов
/ 01 октября 2019

Я реализую функцию обработчика с помощью Spring Web Flux и хочу извлечь значения x-www-form-urlencoded POST-Body.

public Mono<ServerResponse> handle(ServerRequest incomingHttpRequest) {
    Mono<MultiValueMap<String, String>> formData = incomingHttpRequest.body(BodyExtractors.toFormData());
    return formData
            .doOnEach(signal -> {LOG.info("EACH: " + signal.getType());})
            .doOnSubscribe(x -> {LOG.info("SUBSCRIBED");})
            .doOnNext(next -> {LOG.info("NEXT: " + next);})
            .doOnError(x -> {LOG.info("ERROR: " + x.getMessage());})
            .doOnSuccess(x -> {LOG.info("SUCCESS: " + x);})
            .flatMap(multiValueMap -> ServerResponse
                .badRequest()
                .body(BodyInserters.fromObject(
                    "searchTerm: " + multiValueMap.getFirst("searchTerm")
                ))
            );
}

Обработчик добавляется в маршрутизатор как:

return RouterFunctions.route(RequestPredicates.POST("/mysearch"), searchHandler::handle);

Отправка POST с Body searchTerm = что-то & lang = de с помощью Postman дает мне следующий результат:

o.s.w.s.adapter.HttpWebHandlerAdapter    : [f405c02b] HTTP POST "/mysearch"                                                                               
o.s.http.codec.FormHttpMessageReader     : [f405c02b] Read form fields [searchTerm, lang] (content masked)                                                
o.s.w.r.f.s.s.RouterFunctionMapping      : [f405c02b] Mapped to de.myproject...
de.myproject.MyHandler                   : SUBSCRIBED                                                                                                     
de.myproject.MyHandler                   : EACH: onComplete                                                                                               
de.myproject.MyHandler                   : SUCCESS: null                                                                                                  
o.s.w.s.adapter.HttpWebHandlerAdapter    : [f405c02b] Completed 200 OK                                                                                    

Я ожидаю получить BAD_REQUEST, содержащий searchTerm. Но вместо этого я получаю 200.

Мне кажется, что получившийся Mono подписан и затем успешно завершается. Но formData никогда не получает значение и, следовательно, никогда не входит в flatMap(...)

Что я делаю неправильно, отображая Mono<MultiValueMap<String, String>> в Mono<ServerResponse>?

Ответы [ 2 ]

3 голосов
/ 01 октября 2019

Здесь есть две вещи:

  • тот факт, что вы можете подписаться (то есть прочитать) тело запроса только один раз
  • , получая данные формы иззапрос требует чтения и анализа

В Spring Boot 2.0.x-2.1.x по умолчанию HiddenHttpMethodFilter включен. Этот фильтр полезен для использования методов HTTP, которые изначально не поддерживаются браузерами в формах HTML (например, «УДАЛИТЬ»). Этот фильтр должен анализировать данные формы, если входящий запрос относится к этому типу.

Возможно, этот вариант использования встречается все реже и он был отключен по умолчанию с Spring Boot 2.2 .

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

Но лучший способ решить эту проблему - использовать выделенный метод request.formData(), который будет анализировать и кэшировать результат (и возвращать уже проанализированные данные, если вызывается несколько раз). ).

Подробнее об этом можно прочитать в справочной документации Spring Framework .

2 голосов
/ 01 октября 2019

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

Mono<MultiValueMap<String, String>> formData = incomingHttpRequest.formData();

Я проверил, и этот кодекс работает с этим изменением.

...