如何自定义SpringWebFlux WebClient JSON反序列化?

问题描述 投票:0回答:10

我正在使用 spring-webflux WebClient(内部版本 20170502.221452-172)来访问生成 Entry 对象流(application/stream+json)的 Web 应用程序,如下所示:

final WebClient producerClient = WebClient.create("http://localhost:8080/");

Flux<Entry> entries = producerClient.get().uri("json-stream")
        .accept(MediaType.APPLICATION_STREAM_JSON)
        .exchange()
        .flatMapMany(clientResponse -> clientResponse.bodyToFlux(Entry.class));

虽然 Entry 对象的反序列化对于使用标准常见类型(包括 Java 时间(JSR-310)数据类型(如 java.time.Instant)的 POJO 来说效果很好),但我想知道我需要做什么才能将任何自定义 JSON 添加到Java 反序列化(例如,自定义 Jackson ObjectMapper)。

我在 WebClient 或其构建器生成的对象类中找不到任何 API 以及流畅的 API 来执行此操作。

有人用过自定义反序列化的WebClient吗?

(也许API还没有?)

java json spring reactive-programming spring-webflux
10个回答
77
投票

这是一个为 JSON(反)序列化自定义

ObjectMapper
的示例。 请注意,出于流媒体目的,使用了不同的编码器/解码器,但其配置原理保持不变。

    ExchangeStrategies strategies = ExchangeStrategies
            .builder()
            .codecs(clientDefaultCodecsConfigurer -> {
                clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
                clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));

            }).build();

    WebClient webClient = WebClient.builder().exchangeStrategies(strategies).build();

15
投票

根据上面的回复,我最终得到了这段代码:

final ObjectMapper mapper = new ObjectMapper()
    .findAndRegisterModules()
    .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
final ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
    .codecs(configurer -> configurer.defaultCodecs()
    .jackson2JsonDecoder(new Jackson2JsonDecoder(mapper)))
    .build();
final WebClient webClient = WebClient.builder()
    .exchangeStrategies(exchangeStrategies)
    .build();

如果不包含

.findAndRegisterModules()
,当您想要反序列化 Java 8 的时间对象等内容时,就会遇到问题。


10
投票

您可以为特定的 WebClient 进行配置:

@Autowired
public ItunesAlbumServiceImpl(ObjectMapper mapper) {
    ExchangeStrategies strategies = ExchangeStrategies.builder().codecs(clientCodecConfigurer ->
        clientCodecConfigurer.customCodecs().decoder(
                new Jackson2JsonDecoder(mapper,
                        new MimeType("text", "javascript", StandardCharsets.UTF_8)))
    ).build();

    webClient = WebClient.builder()
            .exchangeStrategies(strategies)
            .baseUrl("https://itunes.apple.com")
            .build();
}

但也在“应用程序级别”

通过配置

CodecCustomizer
:

@Bean
public CodecCustomizer jacksonLegacyJsonCustomizer(ObjectMapper mapper) {
    return (configurer) -> {
        MimeType textJavascript = new MimeType("text", "javascript", StandardCharsets.UTF_8);
        CodecConfigurer.CustomCodecs customCodecs = configurer.customCodecs();
        customCodecs.decoder(
                new Jackson2JsonDecoder(mapper, textJavascript));
        customCodecs.encoder(
                new Jackson2JsonEncoder(mapper, textJavascript));
    };
}

这将通过

WebClientAutoConfiguration
作为
WebClient.Builder
bean 生效:

@Autowired
public ItunesAlbumServiceImpl(WebClient.Builder webclientBuilder) {
    webClient = webclientBuilder.baseUrl("https://itunes.apple.com").build();
}

8
投票

全局配置:

@Configuration
public class AppConfig {

    private final ObjectMapper objectMapper;

    @Autowired
    public AppConfig(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
        this.webClientBuilder = WebClient.builder()
                .exchangeStrategies(exchangeStrategies());
    }

    private ExchangeStrategies exchangeStrategies() {
        Jackson2JsonEncoder encoder = new Jackson2JsonEncoder(objectMapper);
        Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(objectMapper);
        return ExchangeStrategies
                .builder()
                .codecs(configurer -> {
                    configurer.defaultCodecs().jackson2JsonEncoder(encoder);
                    configurer.defaultCodecs().jackson2JsonDecoder(decoder);
                }).build();
    }
}

6
投票

从 Spring

5.1.13
开始,您可以使用专用的
.codec
方法来自定义它们:

WebClient.builder()
    .codecs(configurer -> {
        configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
        configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
     })
    .build();

5
投票

使用 webflux 5.0.2,取消注册默认值

val strategies = ExchangeStrategies.builder()
                .codecs { configurer ->
                    configurer.registerDefaults(false)
                    configurer.customCodecs().encoder(Jackson2JsonEncoder(objectMapper, APPLICATION_JSON))
                    configurer.customCodecs().decoder(Jackson2JsonDecoder(objectMapper, APPLICATION_JSON))
                }.build()

1
投票

如果有人有兴趣在测试期间自定义

WebTestClient
,那么这将是类似的方法。只需要对原来的bean进行变异即可。

@Autowired
private WebTestClient webTestClient;

@Test
void test() {
    webTestClient.mutate()
        .codecs(clientCodecConfigurer -> clientCodecConfigurer.defaultCodecs()
            .jackson2JsonDecoder(new Jackson2JsonDecoder(CUSTOM_OBJECT_MAPPER, MediaType.APPLICATION_JSON)))
        .build()
        .get()
        .uri("/test-endpoint")
        .exchange()
        .expectStatus().isOk();


0
投票

如果您使用 Spring Boot,则可以使用自动配置

ObjectMapper
并省略详细的编解码器配置。只需像这样定义 bean 即可:

@Bean
public WebClient webClient(WebClient.Builder builder) {
    return builder.build();
}

正如我所解释的here Spring Boot 会自动配置

WebClient
builder。当您手动创建实例时,它使用内置默认值,然后您必须使用上述方法之一手动更新编解码器。


0
投票

我发现的最直接、简洁和外科手术式的方法是使用 Jackson2ObjectMapperBuilderCustomizer,这是我在 Baeldung 的 Spring Boot:自定义 Jackson ObjectMapper

 中读到的。看起来像这样:

@Configuration
public class MyAppConfiguration {

  @Bean
  public Jackson2ObjectMapperBuilderCustomizer jacksonJsonCustomizer() {
    return builder -> builder.modulesToInstall(/*TODO specify modules here);
  }

}

我已经验证这适用于 Spring Data 以及 WebFlux,例如

WebClient
.bodyToMono(SomeType.class)
。如果您碰巧使用它来自定义
WebClientCustomizer
,它甚至可以与
WebClient
一起使用;请参阅 Spring WebClient.Builder 超时默认值和运行时覆盖 的答案。


0
投票

.exchangeStrategies()
对我来说不起作用,但是通过
.codecs()
直接将其设置到 webClientBuilder 就可以了。

也许您还需要将

configurer.registerDefaults(false)
添加到编解码器配置中并从头开始配置所有内容。

public WebClient build(WebClient.Builder webClientBuilder) {
    return webClientBuilder
            .baseUrl(baseUrl)
            .codecs(this::configureMappers)
            .build();
}

private void configureMappers(ClientCodecConfigurer configurer) {
    configurer.defaultCodecs().jackson2JsonEncoder(
            new Jackson2JsonEncoder(this.objectMapper, MediaType.APPLICATION_JSON));
    configurer.defaultCodecs().jackson2JsonDecoder(
            new Jackson2JsonDecoder(this.objectMapper, MediaType.APPLICATION_JSON));

}
© www.soinside.com 2019 - 2024. All rights reserved.