Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jackson2JsonEncoder and Jackson2JsonDecoder should use provided mime types [SPR-15866] #20421

Closed
spring-issuemaster opened this issue Aug 16, 2017 · 3 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Aug 16, 2017

Ricardo Lindooren opened SPR-15866 and commented

I'm using Webclient to consume a webservice that uses a legacy mime type for JSON: text/javascript.

This works when using the Webclient builder.

@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();
}

But when trying to configure this on application level it doesn't work.

@SpringBootApplication
public class SpringReactiveApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringReactiveApplication.class, args);
    }

    @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));
        };
    }
}

While stepping through the code I noticed that the Jackson2JsonDecoder.getDecodableMimeTypes() and Jackson2JsonEncoder.getEncodableMimeTypes() methods always return the default JSON mime types, not the one(s) provided as constructor argument.

public class Jackson2JsonDecoder extends AbstractJackson2Decoder {

    public Jackson2JsonDecoder() {
        super(Jackson2ObjectMapperBuilder.json().build());
    }

    public Jackson2JsonDecoder(ObjectMapper mapper, MimeType... mimeTypes) {
        super(mapper, mimeTypes);
    }

    @Override
    public List<MimeType> getDecodableMimeTypes() {
        return JSON_MIME_TYPES;
    }
}

Causing Jackson2CodecSupport.supportsMimeType to return false for the provided mime types.

Looks related to #20034

I created this PR with a proposed change: 1499


Affects: 5.0 RC3

Referenced from: commits bb327b9

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 16, 2017

Rossen Stoyanchev commented

hi, small correction. The Jackson2CodecSupport.supportsMimeType method does refer to the list of configured mime types. It's getDecodableMimeTypes and getEncodableMimeTypes that are effectively hard-coded. As a result the BodyExtractors on the client side and the argument resolver on the server side do not perform content negotiation correctly, which is why I also don't follow the comment that it works when using the WebClient.Builder.

Regardless of this, there is an actual issue and the fix looks right. I will however adjust the tests to verify what is actually failing.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 16, 2017

Ricardo Lindooren commented

small correction. The Jackson2CodecSupport.supportsMimeType method does refer to the list of configured mime types
Yes, this was an incorrect observation by me. I modified my pull request comment but forgot to update it here as well.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 16, 2017

Ricardo Lindooren commented

I found out why configuration on application level didn't work for me.

This configuration:

@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));
    };
}

Is picked up by WebClientAutoConfiguration when it configures a base WebClient.Builder
and then exposes the following bean configuration:

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public WebClient.Builder webClientBuilder(List<WebClientCustomizer> customizers) {
    return this.webClientBuilder.clone();
}

So that bean must be used when creating a new Webclient instance.
This then works:

@Autowired
public ItunesAlbumServiceImpl(WebClient.Builder webclientBuilder) {
    webClient = webclientBuilder.baseUrl("https://itunes.apple.com").build();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.