初探RestTemplate--解决中文乱码问题

本文完全承接上一篇文章:初探RestTemplate--postForObject方法直接传递对象,如果你还没有看过,建议先阅读完再看下去。如果你没有兴趣也可以直接拉到本文末尾,查看我对中文乱码的解决方案。

我们在TestController中添加一个新的方法用来接收JSON格式的字符串

 

@RequestMapping(value = "/string", method = RequestMethod.POST)
public String postByJsonString(@RequestBody String jsonString) {
    if (jsonString == null || "".equals(jsonString)) {
        return "FAIL";
    }
    return "SUCCESS:" + jsonString;
}

然后在单元测试类ResttemplateApplicationTests中新增测试方法

 

@Test
public void testString() {
    JSONObject json = new JSONObject();
    json.put("age", 18);
    json.put("name", "小芳");
    json.put("address", "广东深圳");
    RestTemplate restTemplate = RestTemplateUtil.getInstance("utf-8");
    String url = "http://localhost:9999/test/string";
    String result = restTemplate.postForObject(url, json.toJSONString(), String.class);
    System.out.println(result);
}

单独跑testString方法,意外产生了

"D:\Program Files\Java\jdk1.8.0_121\bin\java" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:14875,suspend=y,server=n -ea -Didea.test.cyclic.buffer.size=1048576 -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2017.2.6\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2017.2.6\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2017.2.6\plugins\junit\lib\junit5-rt.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar;D:\ideas\spring-boot-api-project-seed-master\target\test-classes;D:\ideas\spring-boot-api-project-seed-master\target\classes;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-starter-web\1.5.7.RELEASE\spring-boot-starter-web-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-starter\1.5.7.RELEASE\spring-boot-starter-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot\1.5.7.RELEASE\spring-boot-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-starter-logging\1.5.7.RELEASE\spring-boot-starter-logging-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\ch\qos\logback\logback-classic\1.1.11\logback-classic-1.1.11.jar;C:\Users\usher\.m2\repository\ch\qos\logback\logback-core\1.1.11\logback-core-1.1.11.jar;C:\Users\usher\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;C:\Users\usher\.m2\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\Users\usher\.m2\repository\org\slf4j\log4j-over-slf4j\1.7.25\log4j-over-slf4j-1.7.25.jar;C:\Users\usher\.m2\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\1.5.7.RELEASE\spring-boot-starter-tomcat-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.20\tomcat-embed-core-8.5.20.jar;C:\Users\usher\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.20\tomcat-embed-el-8.5.20.jar;C:\Users\usher\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.20\tomcat-embed-websocket-8.5.20.jar;C:\Users\usher\.m2\repository\org\hibernate\hibernate-validator\5.3.5.Final\hibernate-validator-5.3.5.Final.jar;C:\Users\usher\.m2\repository\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;C:\Users\usher\.m2\repository\org\jboss\logging\jboss-logging\3.3.1.Final\jboss-logging-3.3.1.Final.jar;C:\Users\usher\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\usher\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.8.10\jackson-databind-2.8.10.jar;C:\Users\usher\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.8.0\jackson-annotations-2.8.0.jar;C:\Users\usher\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.8.10\jackson-core-2.8.10.jar;C:\Users\usher\.m2\repository\org\springframework\spring-web\4.3.11.RELEASE\spring-web-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\spring-aop\4.3.11.RELEASE\spring-aop-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\spring-beans\4.3.11.RELEASE\spring-beans-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\spring-context\4.3.11.RELEASE\spring-context-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\spring-webmvc\4.3.11.RELEASE\spring-webmvc-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\spring-expression\4.3.11.RELEASE\spring-expression-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\1.5.7.RELEASE\spring-boot-starter-jdbc-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\apache\tomcat\tomcat-jdbc\8.5.20\tomcat-jdbc-8.5.20.jar;C:\Users\usher\.m2\repository\org\apache\tomcat\tomcat-juli\8.5.20\tomcat-juli-8.5.20.jar;C:\Users\usher\.m2\repository\org\springframework\spring-jdbc\4.3.11.RELEASE\spring-jdbc-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\spring-tx\4.3.11.RELEASE\spring-tx-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-starter-test\1.5.7.RELEASE\spring-boot-starter-test-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-test\1.5.7.RELEASE\spring-boot-test-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-test-autoconfigure\1.5.7.RELEASE\spring-boot-test-autoconfigure-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\com\jayway\jsonpath\json-path\2.2.0\json-path-2.2.0.jar;C:\Users\usher\.m2\repository\net\minidev\json-smart\2.2.1\json-smart-2.2.1.jar;C:\Users\usher\.m2\repository\net\minidev\accessors-smart\1.1\accessors-smart-1.1.jar;C:\Users\usher\.m2\repository\org\ow2\asm\asm\5.0.3\asm-5.0.3.jar;C:\Users\usher\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\usher\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\usher\.m2\repository\org\assertj\assertj-core\2.6.0\assertj-core-2.6.0.jar;C:\Users\usher\.m2\repository\org\mockito\mockito-core\1.10.19\mockito-core-1.10.19.jar;C:\Users\usher\.m2\repository\org\objenesis\objenesis\2.1\objenesis-2.1.jar;C:\Users\usher\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\usher\.m2\repository\org\hamcrest\hamcrest-library\1.3\hamcrest-library-1.3.jar;C:\Users\usher\.m2\repository\org\skyscreamer\jsonassert\1.4.0\jsonassert-1.4.0.jar;C:\Users\usher\.m2\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;C:\Users\usher\.m2\repository\org\springframework\spring-core\4.3.11.RELEASE\spring-core-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\org\springframework\spring-test\4.3.11.RELEASE\spring-test-4.3.11.RELEASE.jar;C:\Users\usher\.m2\repository\commons-codec\commons-codec\1.10\commons-codec-1.10.jar;C:\Users\usher\.m2\repository\org\apache\commons\commons-lang3\3.6\commons-lang3-3.6.jar;C:\Users\usher\.m2\repository\com\google\guava\guava\23.0\guava-23.0.jar;C:\Users\usher\.m2\repository\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;C:\Users\usher\.m2\repository\com\google\errorprone\error_prone_annotations\2.0.18\error_prone_annotations-2.0.18.jar;C:\Users\usher\.m2\repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;C:\Users\usher\.m2\repository\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;C:\Users\usher\.m2\repository\mysql\mysql-connector-java\5.1.44\mysql-connector-java-5.1.44.jar;C:\Users\usher\.m2\repository\org\mybatis\mybatis-spring\1.3.1\mybatis-spring-1.3.1.jar;C:\Users\usher\.m2\repository\org\mybatis\mybatis\3.4.5\mybatis-3.4.5.jar;C:\Users\usher\.m2\repository\tk\mybatis\mapper\3.4.2\mapper-3.4.2.jar;C:\Users\usher\.m2\repository\javax\persistence\persistence-api\1.0\persistence-api-1.0.jar;C:\Users\usher\.m2\repository\com\github\pagehelper\pagehelper\4.2.1\pagehelper-4.2.1.jar;C:\Users\usher\.m2\repository\com\github\jsqlparser\jsqlparser\0.9.5\jsqlparser-0.9.5.jar;C:\Users\usher\.m2\repository\com\alibaba\fastjson\1.2.39\fastjson-1.2.39.jar;C:\Users\usher\.m2\repository\com\alibaba\druid-spring-boot-starter\1.1.4\druid-spring-boot-starter-1.1.4.jar;C:\Users\usher\.m2\repository\com\alibaba\druid\1.1.4\druid-1.1.4.jar;D:\Program Files\Java\jdk1.8.0_121\lib\jconsole.jar;D:\Program Files\Java\jdk1.8.0_121\lib\tools.jar;C:\Users\usher\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\1.5.7.RELEASE\spring-boot-autoconfigure-1.5.7.RELEASE.jar;C:\Users\usher\.m2\repository\org\freemarker\freemarker\2.3.23\freemarker-2.3.23.jar;C:\Users\usher\.m2\repository\org\mybatis\generator\mybatis-generator-core\1.3.5\mybatis-generator-core-1.3.5.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.conpany.project.ResttemplateApplicationTests,testString
Connected to the target VM, address: '127.0.0.1:14875', transport: 'socket'
21:57:09.155 [main] DEBUG org.springframework.web.client.RestTemplate - Created POST request for "http://localhost:9999/test/string"
21:57:09.158 [main] DEBUG org.springframework.web.client.RestTemplate - Setting request Accept header to [text/plain, application/json, application/*+json, text/plain, */*, */*]
21:57:09.159 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{"address":"广东深圳","name":"小芳","age":18}] using [org.springframework.http.converter.StringHttpMessageConverter@20b2475a]
21:57:09.440 [main] DEBUG org.springframework.web.client.RestTemplate - POST request for "http://localhost:9999/test/string" resulted in 200 (null)
21:57:09.441 [main] DEBUG org.springframework.web.client.RestTemplate - Reading [java.lang.String] as "text/plain;charset=UTF-8" using [org.springframework.http.converter.StringHttpMessageConverter@20b2475a]
Disconnected from the target VM, address: '127.0.0.1:14875', transport: 'socket'
"SUCCESS:{\"address\":\"????\",\"name\":\"??\",\"age\":18}""SUCCESS:{\"address\":\"????\",\"name\":\"??\",\"age\":18}"

控制台的输入竟然是乱码,这很让人意外,因为上篇文章的测试结果均未出现乱码!

通过跟踪断点,简单分析下RestTemplate内部源码,发现上篇文章以JavaBean,Map,JSONObject格式分别提交数据时,postForObject底层均采用了MappingJackson2HttpMessageConverter来处理请求。而本文以String格式提交数据时,底层其实采用的是StringHttpMessageConverter来处理请求。

所以我们工具类中getInstance方法貌似创建了指定字符集的RestTemplate对象,实则并非如此,这个工具类并不靠谱,一定是我们的getInstance方法出了问题!

查看RestTemplate构造器如下:

StringHttpMessageConverter构造器约定了其默认字符集是ISO-8859-1

回顾我们的getInstance方法创建对象的方式:

 

public static RestTemplate getInstance(String charset) {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName(charset)));
    return restTemplate;
}

这种方式并不靠谱,因为它首先调用了RestTemplate的构造器,这个构造器已经创建了一个默认字符集为ISO-8859-1的StringHttpMessageConverter对象。我们的getInstance 方法只是新增了一个指定字符集的StringHttpMessageConverter而非覆盖,所以提交String格式的post请求使用的字符集仍然是ISO-8859-1。

继续走查源码,发现所有的HttpMessageConverter对象均存放于messageConverters这个集合里面,很清楚的看出来它是个不可变的有序集合ArrrayList。

所以我们对于任何HttpMessageConverter的修改都不应该改变其内在的顺序。

故一方面要保证集合messageConverters中只有一个StringHttpMessageConverter对象,另一方面要保证该对象位置不被改变。我们可以将getInstance方法改造成如下的形式:

 

public static RestTemplate getInstance(String charset) {
    RestTemplate restTemplate = new RestTemplate();
    List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
    for (HttpMessageConverter<?> httpMessageConverter : list) {
        if(httpMessageConverter instanceof StringHttpMessageConverter) {
            ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(Charset.forName(charset));
            break;
        }
    }
    return restTemplate;
}

经过测试,控制台未出现乱码,问题解决。谢谢观看,欢迎指正,若有不足,请不吝赐教。

  • 27
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 26
    评论
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alphathur

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值