RestTemplate——解决Date类型值自动转为时间戳问题
1. 问题描述
使用RestTemplate发送post请求,传参为pojo类集合,pojo类中有Date
数据类型的字段。原本时间值为yyyy-MM-dd HH:mm:ss
格式,但接收端收到的是时间戳格式。
@Autowired
protected RestTemplate restTemplate;
List<User> list = new ArrayList<>();
User user = new User();
user.setCreateTime(new Date());
list.add(user);
restTemplate.postForObject(url, list, JSONObject.class);
2. 网上答案
-
加配置
spring: jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss
配置原本就存在了,所以这个答案无效。
-
在字段上添加
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
这个字段的作用是在转换成JSON的时候规定时间格式,跟当前问题无直接关系。
但是可以通过先转换为JSON字符串,然后将JSON字符串做为参数去请求,这样是可以解决自动转时间戳问题的
String params = JSON.toJSONString(list); restTemplate.postForObject(url, params, JSONObject.class);
但这样就需要在每个pojo类的Date数据类型字段上面添加注解,并不是很好的解决方法,这里寻找可以全局设定的解决方案。
3. 解决路程
-
确定是发送端还是接收端转换成时间戳的
在
restTemplate.postForObject()
发送请求时打断点,可以看到参数已经是时间戳了,所以确定是在发送端将参数转换成了时间戳。 -
restTemplate.postForObject()打断点分析是哪里改成了时间戳
源码关键代码:
@Nullable public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = this.httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor(responseType, this.getMessageConverters(), this.logger); return this.execute(url, HttpMethod.POST, requestCallback, responseExtractor, (Object[])uriVariables); } public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable Type responseType) { super(responseType); if (requestBody instanceof HttpEntity) { this.requestEntity = (HttpEntity)requestBody; } else if (requestBody != null) { this.requestEntity = new HttpEntity(requestBody); } else { this.requestEntity = HttpEntity.EMPTY; } } @Nullable protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException { Assert.notNull(url, "URI is required"); Assert.notNull(method, "HttpMethod is required"); ClientHttpResponse response = null; Object var14; try { ClientHttpRequest request = this.createRequest(url, method); if (requestCallback != null) { requestCallback.doWithRequest(request); } response = request.execute(); this.handleResponse(url, method, response); var14 = responseExtractor != null ? responseExtractor.extractData(response) : null; } catch (IOException var12) { String resource = url.toString(); String query = url.getRawQuery(); resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource; throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + var12.getMessage(), var12); } finally { if (response != null) { response.close(); } } return var14; } public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { super.doWithRequest(httpRequest); Object requestBody = this.requestEntity.getBody(); if (requestBody == null) { HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); if (!requestHeaders.isEmpty()) { requestHeaders.forEach((key, values) -> { httpHeaders.put(key, new LinkedList(values)); }); } if (httpHeaders.getContentLength() < 0L) { httpHeaders.setContentLength(0L); } } else { Class<?> requestBodyClass = requestBody.getClass(); Type requestBodyType = this.requestEntity instanceof RequestEntity ? ((RequestEntity)this.requestEntity).getType() : requestBodyClass; HttpHeaders httpHeadersx = httpRequest.getHeaders(); HttpHeaders requestHeadersx = this.requestEntity.getHeaders(); MediaType requestContentType = requestHeadersx.getContentType(); Iterator var8 = RestTemplate.this.getMessageConverters().iterator(); while(var8.hasNext()) { HttpMessageConverter<?> messageConverter = (HttpMessageConverter)var8.next(); if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<Object> genericConverter = (GenericHttpMessageConverter)messageConverter; if (genericConverter.canWrite((Type)requestBodyType, requestBodyClass, requestContentType)) { if (!requestHeadersx.isEmpty()) { requestHeadersx.forEach((key, values) -> { httpHeadersx.put(key, new LinkedList(values)); }); } this.logBody(requestBody, requestContentType, genericConverter); genericConverter.write(requestBody, (Type)requestBodyType, requestContentType, httpRequest); return; } } else if (messageConverter.canWrite(requestBodyClass, requestContentType)) { if (!requestHeadersx.isEmpty()) { requestHeadersx.forEach((key, values) -> { httpHeadersx.put(key, new LinkedList(values)); }); } this.logBody(requestBody, requestContentType, messageConverter); messageConverter.write(requestBody, requestContentType, httpRequest); return; } } String message = "No HttpMessageConverter for " + requestBodyClass.getName(); if (requestContentType != null) { message = message + " and content type \"" + requestContentType + "\""; } throw new RestClientException(message); } }
从源码可以看出,在发送之前会进行参数转换
Iterator var8 = RestTemplate.this.getMessageConverters().iterator(); while(var8.hasNext()) { HttpMessageConverter<?> messageConverter = (HttpMessageConverter)var8.next(); if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<Object> genericConverter = (GenericHttpMessageConverter)messageConverter; if (genericConverter.canWrite((Type)requestBodyType, requestBodyClass, requestContentType)) { if (!requestHeadersx.isEmpty()) { requestHeadersx.forEach((key, values) -> { httpHeadersx.put(key, new LinkedList(values)); }); } this.logBody(requestBody, requestContentType, genericConverter); genericConverter.write(requestBody, (Type)requestBodyType, requestContentType, httpRequest); return; } } else if (messageConverter.canWrite(requestBodyClass, requestContentType)) { if (!requestHeadersx.isEmpty()) { requestHeadersx.forEach((key, values) -> { httpHeadersx.put(key, new LinkedList(values)); }); } this.logBody(requestBody, requestContentType, messageConverter); messageConverter.write(requestBody, requestContentType, httpRequest); return; } }
而这里转换器实现类为
MappingJackson2HttpMessageConverter
,而一开始的配置就是配置该类的,那就是说配置没有生效。于是,使用Java代码配置
MappingJackson2HttpMessageConverter
@Bean public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); ObjectMapper mapper = new ObjectMapper(); mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); converter.setObjectMapper(mapper); return converter; }
结果还是不生效,这时候可以将注意力转到RestTemplate配置了;
@Bean public RestTemplate restTemplate() { return new RestTemplate(); }
这样初始化RestTemplate,会重新创建一个MappingJackson2HttpMessageConverter对象
public RestTemplate() { this.messageConverters = new ArrayList(); this.errorHandler = new DefaultResponseErrorHandler(); this.headersExtractor = new RestTemplate.HeadersExtractor(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter(false)); try { this.messageConverters.add(new SourceHttpMessageConverter()); } catch (Error var2) { } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); if (romePresent) { this.messageConverters.add(new AtomFeedHttpMessageConverter()); this.messageConverters.add(new RssChannelHttpMessageConverter()); } if (jackson2XmlPresent) { this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); } else if (jaxb2Present) { this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } if (jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } else if (gsonPresent) { this.messageConverters.add(new GsonHttpMessageConverter()); } else if (jsonbPresent) { this.messageConverters.add(new JsonbHttpMessageConverter()); } if (jackson2SmilePresent) { this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter()); } if (jackson2CborPresent) { this.messageConverters.add(new MappingJackson2CborHttpMessageConverter()); } this.uriTemplateHandler = initUriTemplateHandler(); }
所以问题转化为RestTemplate怎样初始化可以用到当前的MappingJackson2HttpMessageConverter对象
@Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); }
4. 总结
在application.yaml
中设置jackson时间格式是全局解决该问题的方法,但是要RestTemplate初始化正确,也就是使用到了当前的MappingJackson2HttpMessageConverter,而不是重新初始化。
世界那么大,感谢遇见,未来可期…
欢迎同频共振的那一部分人
作者公众号:Tarzan写bug
淘宝店:提供一元解决Java问题和其他方面的解决方案,欢迎咨询