2

什么是自动编组/解组 XML 的“Boot-iful”方式?

我的休息控制器:

@RestController
@Slf4j
public class ProvisioningController {

    @RequestMapping(value = "/provisioning", method = RequestMethod.POST,
                    consumes = MediaType.APPLICATION_XML)
    public void handleRequest(@RequestBody ProvisioningRequest request) {
        log.info("Rx request: {}", request);
    }
}

ProvisioningRequest 类由 Jaxb 从 XSD 生成。

将示例请求发布到其余端点会出现以下错误:

java.lang.IllegalStateException: Failed to resolve argument 0 of type 'jaxb.generated.ProvisioningRequest' on public void rest.controller.ProvisioningController.handleRequest(jaxb.generated.ProvisioningRequest)

我在类路径中有'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'。

配置 @Bean Jackson2ObjectMapperBuilder 似乎也无济于事。

我错过了什么吗?

=== 完整的堆栈跟踪:

 java.lang.IllegalStateException: Failed to resolve argument 0 of type 'jaxb.generated.ProvisioningRequest' on public void rest.controller.ProvisioningController.handleRequest(jaxb.generated.ProvisioningRequest) throws java.io.IOException
    at org.springframework.web.reactive.result.method.InvocableHandlerMethod.getArgumentError(InvocableHandlerMethod.java:198) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.web.reactive.result.method.InvocableHandlerMethod.resolveArg(InvocableHandlerMethod.java:193) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$null$1(InvocableHandlerMethod.java:149) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at java.util.Optional.orElseGet(Optional.java:267) ~[na:1.8.0_73]
    at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$resolveArguments$2(InvocableHandlerMethod.java:147) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_73]
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) ~[na:1.8.0_73]
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_73]
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_73]
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_73]
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_73]
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) ~[na:1.8.0_73]
    at org.springframework.web.reactive.result.method.InvocableHandlerMethod.resolveArguments(InvocableHandlerMethod.java:153) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.web.reactive.result.method.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:117) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.lambda$handle$3(RequestMappingHandlerAdapter.java:312) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at reactor.core.publisher.MonoThenMap$MonoThenApplyMain.onNext(MonoThenMap.java:100) [reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:928) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore$MonoThenIgnoreMain.drain(MonoThenIgnore.java:145) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore.subscribe(MonoThenIgnore.java:54) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenMap.subscribe(MonoThenMap.java:57) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenMap$MonoThenApplyMain.onNext(MonoThenMap.java:130) [reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:68) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:78) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:263) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:743) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:119) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:928) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore$MonoThenAcceptSubscriber.onNext(MonoThenIgnore.java:261) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:1657) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore$MonoThenAcceptSubscriber.onSubscribe(MonoThenIgnore.java:250) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:59) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore$MonoThenIgnoreMain.drain(MonoThenIgnore.java:151) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore.subscribe(MonoThenIgnore.java:54) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoMapFuseable.subscribe(MonoMapFuseable.java:60) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:402) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:203) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:97) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:57) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:126) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:41) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoOtherwiseIfEmpty.subscribe(MonoOtherwiseIfEmpty.java:44) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenMap.subscribe(MonoThenMap.java:57) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenMap.subscribe(MonoThenMap.java:57) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoOtherwise.subscribe(MonoOtherwise.java:44) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoOtherwise.subscribe(MonoOtherwise.java:44) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoOtherwise.subscribe(MonoOtherwise.java:44) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore$MonoThenIgnoreMain.drain(MonoThenIgnore.java:169) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at reactor.core.publisher.MonoThenIgnore.subscribe(MonoThenIgnore.java:54) ~[reactor-core-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.http.server.reactive.ServletHttpHandlerAdapter.service(ServletHttpHandlerAdapter.java:106) ~[spring-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:798) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[na:1.8.0_73]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_73]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
    at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_73]
Caused by: java.lang.UnsupportedOperationException: null
    at org.springframework.core.codec.AbstractDecoder.decodeToMono(AbstractDecoder.java:64) ~[spring-core-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.http.codec.DecoderHttpMessageReader.readMono(DecoderHttpMessageReader.java:84) ~[spring-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:160) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.web.reactive.result.method.annotation.RequestBodyArgumentResolver.resolveArgument(RequestBodyArgumentResolver.java:78) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    at org.springframework.web.reactive.result.method.InvocableHandlerMethod.resolveArg(InvocableHandlerMethod.java:184) ~[spring-webflux-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
    ... 66 common frames omitted
4

2 回答 2

1

在 WebFluxJaxb2XmlDecoder中没有实现decodeToMono,但现在已通过SPR-16759 修复。因此,只需升级到 Spring Framework 5.0.6+ / Spring Boot 2.0.2+ 即可避免报告的异常。

于 2018-05-02T11:50:19.977 回答
0

要进行正确的序列化/反序列化,您可以设置自定义编码器/解码器。请参阅下面的示例。

一个简短的理论:

要使用自定义(并且只有自定义)反序列化器,请执行以下覆盖:

在 Kotlin 上:

@Bean
open fun webFluxConfigurer(myReader: MyReader,                               
                           myWriter: MyWriter): WebFluxConfigurer {
    return object : WebFluxConfigurer {
        override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
            // disable default codecs, because of problematic XML serialization in Jaxb2XmlDecoder
            configurer.registerDefaults(false)

            configurer.customCodecs().decoder(myReader)

            configurer.customCodecs().encoder(myWriter)
        }
    }

}

或者在 Java 上:

@Bean
WebFluxConfigurer webFluxConfigurer(MyReader myReader, MyWriter myWriter){ 
    return new WebFluxConfigurer() {
        @Override
        public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
            // disable default codecs, because of problematic XML serialization in Jaxb2XmlDecoder
            configurer.registerDefaults(false);

            configurer.customCodecs().decoder(myReader);

            configurer.customCodecs().encoder(myWriter);
        }
    };

}
于 2018-04-21T14:39:46.323 回答