您的位置:首页 > 编程语言 > Java开发

spring组件 RestTemplate + @ResponseBody 使用心得

2018-02-27 17:17 931 查看
刚刚给公司两个系统写了一个中间件,用于同步一些数据,用了 RestTemplate 这个家伙,确实相当友好,也简化了 http 请求,但是我也走了很多弯路,也被网上的那些各种不完整的博客坑惨了,下面说重点:

一. RestTemplate 简单配置

@Configuration
public class RestTemplateConfig {
@Bean
@ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
<!--使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")-->
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();

<!--!!!!!!!!!!严重警告,一定不要这么配置-->
<!--这样就改变的convert的顺序,当接口采用@requestbody接收的时候,会报 parse error-->
<!--while (iterator.hasNext()) {-->
<!--    HttpMessageConverter<?> converter = iterator.next();-->
<!--    if (converter instanceof StringHttpMessageConverter) {-->
<!--        iterator.remove();-->
<!--    }-->
<!--}-->
<!--messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));-->

<!--正确姿势-->
while (iterator.hasNext()) {
HttpMessageConverter<?> converter = iterator.next();
if (converter instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) converter).setDefaultCharset(Charset.forName("utf-8"));
}
}
return restTemplate;
}

@Bean
@ConditionalOnMissingBean({ClientHttpRequestFactory.class})
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(15000);
factory.setConnectTimeout(15000);
return factory;
}
}


二. 依赖注入

@Autowired
private RestTemplate restTemplate;


有了上面这些,我想需要写工具类,直接 new 一个来使用,或者是写 xml 配置文件来依赖注入都不成问题了

三. 使用

1.第一步请先看看官方文档,别再网上搜,网上的东西都是针对使用者的环境能用,到你那里不一定就能用的

官方文档:https://docs.spring.io/spring/docs/5.0.2.RELEASE/spring-framework-reference/integration.html#rest-client-access

2.@ResponseBody 返回的是 json 字符串,但是 RestTemplate 在 postForObject 的时候,如果返回参数里面包含 List 等组数格式字符串,同时这个数组你用了 object 对象来接收,就会出现结果只有 list 的最后一个值,前面的值都丢失了。如果用了 List 的格式接收就没有这个问题。

解决方法:

import com.alibaba.fastjson.JSON;
import springfox.documentation.spring.web.json.Json;

@RestController
@RequestMapping("/customer")
public class CustomerController {

@Autowired private RestTemplate restTemplate;

@PostMapping("/push")
//接收参数请设置为 json 接收:即给接收参数加上 @RequestBody 注解,可以简化 RestTemplate 传递的参数封装
public Object push(@RequestBody List<Customer> customers) {
//将返回对象包装为 Json 对象返回,而不是 json 字符串
return new Json(JSON.toJSONString(Object object));
}


3.完整的 RestTemplate 请求:

//这里用的是 postForObject ,其他请求方式大同小异
//因为前面接收使用了 @RequestBody  注解方式,以 json 格式接收,这里的请求参数 object 不需要任何封装,直接使用就行了
Object object = new Object();
ResponseResult entity =
restTemplate.postForObject("http://192.168.82.15:9900/pool/customer/list", object , ResponseResult.class);

//此时 entity.getData() 实际上是一个 json 字符串
//转化对象内的 Object 对象
Page<Customer> ids = com.alibaba.fastjson.JSONObject.parseObject(entity.getData().toString(), Page.class);
//转化对象内的 Array 对象
List<Long> ids = com.alibaba.fastjson.JSONArray.parseArray(entity.getData().toString(), Long.class);

System.out.println(ids);

return ids;


我的 ResponseResult 对象:
@ApiModel("响应结果对象")
public class ResponseResult implements Serializable {
@ApiModelProperty("响应码")
private Integer code = ResponseCode.SUCCESS;
@ApiModelProperty("响应消息")
private String msg;
@ApiModelProperty("响应数据")
private Object data;
@ApiModelProperty("响应时间")
private Date time = new Date();
...
}


四. 双重序列化问题:

当使 RestTemplate 返回的参数,如:我上面包装的对象 ResponseResult 的 Data ,有可能是比较复杂的对象,Data 接收到的参数就会直接以 json 串的形式保留下来,如果此时不对该 json 串做正确的反序列化,直接将该 json 串通过接口再发送给其他调用者。问题来了,第三方接到的 Data 会以带转义符的 json 串呈现,正确的做法是:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sword.ttttttttttt.entity.Student;

import java.io.IOException;
import java.util.ArrayList;

public class TestMain {

public static void main(String[] args) {
Student s = new Student();
s.setName("abc");
s.setId(1);
s.setAge(null);
s.setList(new ArrayList<String>());

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
try {
//第一次序列化
String ss = objectMapper.writeValueAsString(s);
//第二次序列化
String string = objectMapper.writeValueAsString(ss);
//反序列化第二次的序列化,而不是用replace或者substring之类的方法来解析
String student = objectMapper.readValue(string, String.class);
//反序列化第一次的序列化,得到正确的对象
Student object = objectMapper.readValue(student, Student.class);

} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

}


ps:跟踪了一下源代码,没有找到最初的数据是从哪儿获取出来,又是从哪儿开始反序列化的,如果有知道的朋友,还请留步!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring resttemplate