1. Problem symptoms

Result<List<UserInfo>> result = restTemplate
				.exchange("http://127.0.0.1:8080/user/t/save", HttpMethod.POST, new HttpEntity<>(params), new ParameterizedTypeReference<Result<List<UserInfo>>>() {
				}).getBody();
Copy the code
private Date createTime;
Copy the code

The RestTemplate interface is used to request an object to receive and return a packet, in which the time string in the packet is received as Date.

org.springframework.web.client.RestClientException: Error while extracting response for type [com.example.demo.bean.Result<java.util.List<com.example.demo.domain.UserInfo>>] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "The 2021-05-27 09:41:27": not a valid representation (error: Failed to parse Date value 'the 2021-05-27 09:41:27': Cannot parse date "The 2021-05-27 09:41:27": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "The 2021-05-27 09:41:27": not a valid representation (error: Failed to parse Date value 'the 2021-05-27 09:41:27': Cannot parse date "The 2021-05-27 09:41:27": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))
 at [Source: (PushbackInputStream); line: 1, column: 51] (through reference chain: com.example.demo.bean.Result["data"]->java.util.ArrayList[0]->com.example.demo.domain.UserInfo["createTime"])
	at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:120)
	at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:998)
	at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:981)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:741)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:612)
Copy the code

You can see that the Jackson serialization failed due to the incorrect time format.

There are two ways we know how to handle time format serialization in Spring Boot:

1. Configure the global Jackson time format in application.yml

spring:
  jackson:
        # date formatting
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
Copy the code

2. Add @dateTimeFormat and @jsonFormat to the time member variable

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createTime;
Copy the code

After this problem occurs, the configuration in application.yml is first checked to confirm that it is normal.

Then try to add formatting annotations to the field, test the call, and the application works. But the need to annotate each time field is too laborious and clearly not what we want.

2. Troubleshooting process

It is suspected that the configuration in YML does not take effect when the RestTemplate processes parameters. According to the exception stack information, locating the HttpMessageConverterExtractor, hit a breakpoint, the DEBUG tracking

1. Guess and verify

You can see that this object has objectMapper.

If we look at the DateFormat in ObjectMapper, we can see that the properties are all default values, and our configuration in YML is not effective.

2. The Jasckson configuration is loaded

Through the above procedure, we have seen that Jackson does not use our configuration information in YML when processing parameters. Therefore, it is suspected that the Jackson parameter was not loaded when the application was started. Find the startup classJacksonAutoConfigurationAnd found that theJackson2ObjectMapperBuilderCreate ObjectMapper and findJacksonProperties

Set a breakpoint and restart application debugging.

Find configuration is loaded, not yml configuration problem, but MappingJackson2HttpMessageConverter, when using configuration has no effect.

Check MappingJackson2HttpMessageConverter again and found that there are two constructors, on breakpoints, restart the application debugging found two constructors, are carried out and performed several times. Suspect that this object is not a singleton and that the RestTemplate has its own unique. Look at the RestTemplate and see that this is true.

if (jackson2Present) {
	addPartConverter(new MappingJackson2HttpMessageConverter());
}
Copy the code

Problem solving

The RestTemplate problem can now be identified by checking the registration mode of the RestTemplate in the project

	@Bean
	public RestTemplate restTemplate(a) {
		return new RestTemplate();
	}
Copy the code

Although I am not familiar with RestTemplate, in Spring Boot projects, such configuration is usually constructed by Builder, directly new objects, most likely the object attributes are default values, and the project configuration cannot be read. The correct registration method of the RestTemplate is as follows

	@Bean
	public RestTemplate restTemplate(RestTemplateBuilder builder) {
		return builder.build();
	}
Copy the code

Restart application verification.

4. New problems

After identifying the cause of the problem and finding a solution, I changed the RestTemplate construction mode of all projects, but unexpectedly, it caused a new problem. Some colleagues reported that the GateWay failed to start.

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method restTemplate in com.example.demo.config.RestTemplateConfig required a bean of type 'org.springframework.boot.web.client.RestTemplateBuilder' that could not be found.


Action:

Consider defining a bean of type 'org.springframework.boot.web.client.RestTemplateBuilder' in your configuration.

Copy the code

Error message indicating RestTemplateConfig could not be found. Open the RestTemplateAutoConfiguration

	static class NotReactiveWebApplicationCondition extends NoneNestedConditions {

		NotReactiveWebApplicationCondition() {
			super(ConfigurationPhase.PARSE_CONFIGURATION);
		}

		@ConditionalOnWebApplication(type = Type.REACTIVE)
		private static class ReactiveWebApplication {}}Copy the code

Spring Boot is divided into MVC (NoReactive) and WebFlux (Reactive). Check the GateWay project POM file to check whether there is a WebFlux dependency. Re-check the application project and find that the dependency also has WebFlux, but it works fine.

Tested: There is Web and WebFlux and the project is NoReactive. Only WebFlux is Reactive.

The WebFlux engineering interface invokes the application using WebClient.

3. Summary

  1. You should follow the specification when using an API you don’t understand.

  2. When introducing an UNKNOWN API, you should do adequate testing for each scenario.