FixBug diary: I was bitten by some tools

Posted by ShadowIce on Wed, 23 Feb 2022 15:34:17 +0100

preface

I've been interfacing with some platforms recently, and then I use restTemplate to request
First of all, of course, according to the document, and then postman to adjust it. They are all successful. Good ~ then, I use this tool to realize remote call, and then always report that contentType is not supported, but I have set the same request header

Check the scene

  1. Popularize knowledge
    When we do component specialization configuration, we should avoid affecting other people's use. For example, if two implementation classes implement a parent class, an error will be reported when injecting

    Solution
    @Bean s write different injection method names, and then @ quality points to the corresponding implementation class
    @The Primary annotation mainly tells spring which class to use first

  2. Start troubleshooting
    There must be no problem with the code. I'm so confident~
    The following figure shows the form format submission, which is very simple. Why has it been said that it is not supported? The document also says to use this header header

  3. debug mode
    You can see that there are many converters in restTemplate

    The key is FormHttpMessageConverter

  4. FormHttpMessageConverter
    In the code, the header will judge the character encoding type, and throw an exception if it doesn't

  5. See the getFormContentType method
    If no character set is set in this method, we will be given the set character set by default

Solve a case

The docking document is also wonderful. If you add charse=utf-8 to the contentType, an error will be reported
resttemplate is also a wonderful work. I have to add a character set to you

I'm just going to the end

resolvent

thinking
There are many converters, so we get the corresponding converter list, secretly replace it, and then rewrite the method. Another thorny problem is that if I set a MediaType without character set and fail to pass the judgment, in fact, we can use the reflection method to insert it!

Open dry

@Bean
public RestTemplate beiSenTokenRestTemplate() {
    RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory(new OkHttpClient().newBuilder()
            // Connection establishment timeout
            .connectTimeout(5, TimeUnit.SECONDS)
            // Read data timeout
            .readTimeout(60, TimeUnit.SECONDS)
            // Write data timeout
            .writeTimeout(60, TimeUnit.SECONDS)
            // Certificate verification
            .sslSocketFactory(SSL.getSSLSocketFactory(), SSL.getX509TrustManager())
            // Host verification
            .hostnameVerifier(SSL.getHostnameVerifier())
            .build()));
    List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
    list.removeIf(item -> item instanceof FormHttpMessageConverter);
    list.add(new FormHttpMessageConverter() {

        @Override
        protected MediaType getFormContentType(MediaType contentType) {
            MediaType type = new MediaType(MediaType.APPLICATION_FORM_URLENCODED);

            if (contentType == null) {
                return new MediaType(MediaType.APPLICATION_FORM_URLENCODED, DEFAULT_CHARSET);
            } else if (type.getType().equals(contentType.getType())) {
                setCharset(StandardCharsets.UTF_8);

                try {
                    Field fi = MediaType.class.getSuperclass().getDeclaredField("resolvedCharset");
                    fi.setAccessible(true);
                    fi.set(type, StandardCharsets.UTF_8);
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    return new MediaType(contentType, DEFAULT_CHARSET);
                }
                return type;
            } else if (contentType.getCharset() == null) {
                return new MediaType(contentType, DEFAULT_CHARSET);
            } else {
                return contentType;
            }
        }
    });
    return restTemplate;
}
List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
    list.removeIf(item -> item instanceof FormHttpMessageConverter);

This is to replace the form converter and insert a new one
Then override the FormHttpMessageConverter getFormContentType method if the contentType and Application_ FORM_ When urlencoded is the same, we will
resolvedCharset in MediaType gives a default value

Why is mediatype class. getSuperclass()?
Because resolvedCharset is the value of its parent class

Topics: Java RestTemplate