Ошибки обработчика веб-потока Spring Boot при декодировании содержимого тела XML-запроса в объект Mono или Flux

#spring-boot #jaxb #handler #spring-webflux #spring-reactive

#spring-boot #jaxb #обработчик #spring-webflux #spring-реактивный

Вопрос:

Я пытался реализовать Spring реактивные маршрутизаторы и обработчики вместо контроллеров MVC. Казалось бы, достаточно просто, но мой тест WebTestClient не удался с внутренней ошибкой сервера 500. Короткий код по сравнению с тестом.

 @ContextConfiguration(classes = {FooRouter.class, FooServiceConfig.class, FoonHandler.class})
@WebFluxTest
public class FooHandlerTest {

@Test
public void testFooPost()  {
    webTestClient.post()
            .uri("/foot")
            .contentType(MediaType.APPLICATION_XML)
            .accept(MediaType.APPLICATION_XML)
            .body(Mono.just(foo), Foo.class)
            .exchange()
            .expectStatus().isCreated()
            .expectBody(Bar.class)
            .isEqualTo(bar);
}
  

Во время теста маршрутизатор работает нормально и вызывает обработчик. Когда обработчик пытается создать Mono из тела запроса:

 Mono<Foo> fooMono = request.bodyToMono(Foo.class);
  

эта строка завершает модульный тест, и WebTestClient получает код состояния 500. Во время отладки, хотя mime-типом является application / xml и он отображал класс Java, BodyExtractors фильтрует декодеры и сопоставляет класс и тип mime. Для Json он просматривает класс Java и пытается получить от него сопоставление, прежде чем сравнивать тип mime. В глубине отладки происходит сбой, когда он все еще пытается сопоставить класс с object mapper.

В настоящее время я использую Spring Boot версии 2.3.4-RELEASE с Java8. В документации упоминается, что если в вашем пути к классу есть JaxB, то для XML он должен использовать Jaxb2XmlDecoder и Endoder для кодеков по умолчанию. Он также будет использовать Jackson, если он есть в вашем пути к классу. У меня действительно есть оба минус зависимость jackson-dataformat-xml. Я почти уверен, что это либо моя тестовая конфигурация, либо это может быть WebFluxConfigurer, который я попытался установить кодек по умолчанию для jaxb2Decoder и Encoder. Я также попытался добавить зависимость jackson-dataformat-xml, но это тоже не сработало. Это webconfig, который я пытался использовать, но все еще сбой при попытке декодировать mime-типы application / xml с помощью декодера Json. Вот webconfig:

 @Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        configurer.registerDefaults(false);
        configurer.defaultCodecs().jaxb2Decoder(new Jaxb2XmlDecoder(MediaType.APPLICATION_XML));
        configurer.defaultCodecs().jaxb2Encoder(new Jaxb2XmlEncoder());
    }
}
  

Я отлаживал и распечатывал средства чтения сообщений request objects, а Jaxb2XmlEncoder является одним из кодеков для mime-типа XML. Вот сообщение о печати для декодеров JSON и XML:

 Message Reader: org.springframework.http.codec.DecoderHttpMessageReader@5b2cea27
    DecoderHttpMessageReader Decoder: org.springframework.http.codec.json.Jackson2JsonDecoder@87b7472
        Mime Type: application/json
        Mime Type: application/* json
Message Reader: org.springframework.http.codec.DecoderHttpMessageReader@198a86e1
    DecoderHttpMessageReader Decoder: org.springframework.http.codec.xml.Jaxb2XmlDecoder@50cb41d0
        Mime Type: application/xml
        Mime Type: text/xml
        Mime Type: application/* xml
  

Когда в методе readWithMessageReaders в классе BodyExtractors в функции есть фильтр, который выполняет итерацию по считывателям и декодерам сообщений. Когда он попадает в DecoderHttpMessageReader, Jackson2JsonDecoder, он начинает пытаться декодировать запрос. Не уверен, почему, поскольку тип mime в отладчике показывает application / xml.

Есть ли у кого-нибудь предложения по отладке этого фрагмента кода:

 Mono<Foo> fooMono = request.bodyToMono(Foo.class);
  

Именно здесь он умирает во время модульного тестирования с приведенным выше кодом. Я пробовал отладку, но это просто остановит тест, когда я зайду слишком глубоко. Я просто знаю, что он проходит через тип Java, когда пытается сопоставить кодек. Большинство кодеков просто сразу возвращаются, но декодеры Json и XML пытаются получить атрибуты класса Java и задыхаются. Класс является классом JaxB со всеми надлежащими аннотациями. У WebTestClient нет проблем с кодированием класса в XML для теста. Так почему обработчик не может просто преобразовать тело в mono? Из этого оператора кода ничего не выбрасывается.

Ответ №1:

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

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

Старый оператор возврата:

 return created(URI.create(response.getDocument().getPath()))
    .bodyValue(Mono.just(response));
  

Новый оператор возврата:

 return created(URI.create(response.getDocument().getPath()))
    .body(Mono.just(response), Response.class);
  

Видите разницу с bodyValue и просто body? Это вызвало невозможность преобразования тела запроса в Mono.