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-projects-issues opened this issue Aug 16, 2017 · 3 comments
Assignees
Labels
in: web type: bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues 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-projects-issues
Copy link
Collaborator Author

spring-projects-issues 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-projects-issues
Copy link
Collaborator Author

spring-projects-issues 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-projects-issues
Copy link
Collaborator Author

spring-projects-issues 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();
}

@spring-projects-issues spring-projects-issues added type: bug in: web labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.0 RC4 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web type: bug
Projects
None yet
Development

No branches or pull requests

2 participants