Solutions that use Feign in Spring Cloud @ RequestBody cannot inherit
- 2021-11-30 00:02:50
- OfStack
Unable to inherit with Feign, @ RequestBody
According to the example of official website FeignClient, write a simple updateUser interface, which is defined as follows
@RequestMapping("/user")
public interface UserService {
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
UserDTO findUserById(@PathVariable("userId") Integer userId);
@RequestMapping(value = "/update", method = RequestMethod.POST)
boolean updateUser(@RequestBody UserDTO user);
}
Implementation class
@Override
public boolean updateUser(UserDTO user)
{
LOGGER.info("===updateUser, id = " + user.getId() + " ,name= " + user.getUsername());
return false;
}
Execute the unit test and find that the expected input parameters are not obtained
2018-09-07 15:35:38,558 [http-nio-8091-exec-5] INFO [com.springboot.user.controller.UserController] {} - ===updateUser, id = null ,name= null
Cause analysis
In SpringMVC, RequestResponseBodyMethodProcessor class is used to parse the input and output parameters. The following method determines whether to convert the message body based on whether the parameter has an @ RequestBody annotation.
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
Solutions
Since MVC uses RequestResponseBodyMethodProcessor for parameter analysis, a customized Processor can be realized, and the judgment method of supportParameter can be modified.
@Override
public boolean supportsParameter(MethodParameter parameter)
{
//springcloud The interface entry parameter of does not write @RequestBody And is a custom type object Also press JSON Analyse
if (AnnotatedElementUtils.hasAnnotation(parameter.getContainingClass(), FeignClient.class) && isCustomizedType(parameter.getParameterType())) {
return true;
}
return super.supportsParameter(parameter);
}
The judgment logic here can be defined according to the actual framework, and the purpose is to judge the interface defined for Spring Cloud and use the same content converter as @ RequestBody when it is a custom object.
After implementing the customized Processor, you need to make the customized configuration take effect. There are two options:
Replace RequestResponseBodyMethodProcessor directly, and customize RequestMappingHandlerAdapter under SpringBoot. Implementation of addArgumentResolvers Interface in WebMvcConfigurerHere, the simpler second method is adopted, and the message converter at initialization is loaded as needed:
public class XXXWebMvcConfig implements WebMvcConfigurer
{
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)
{
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(5);
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringHttpMessageConverter);
messageConverters.add(new SourceHttpMessageConverter<>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
CustomizedMappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new CustomizedMappingJackson2HttpMessageConverter();
jackson2HttpMessageConverter.setObjectMapper(defaultObjectMapper());
messageConverters.add(jackson2HttpMessageConverter);
ViomiMvcRequestResponseBodyMethodProcessor resolver = new ViomiMvcRequestResponseBodyMethodProcessor(messageConverters);
resolvers.add(resolver);
}
After the modification is completed, the @ RequestBody annotation can be removed from the implementation class of microservice.
Problems with feign
The construction of spring cloud using feign project will not be written here. This article mainly explains the problems encountered in the use process and the solutions
1. Example
@RequestMapping(value = "/generate/password", method = RequestMethod.POST)
KeyResponse generatePassword(@RequestBody String passwordSeed);
Only @ RequestMapping (value = "/generate/password", method = RequestMethod. POST) Annotations cannot be used here
@ PostMapping otherwise the project kick-off meeting will be reported
Caused by: java. lang. IllegalStateException: Method generatePassword not annotated with HTTP method type (ex. GET, POST) Exception
2. The first access timeout problem
Reason: The default timeout for Hystrix is 1 second. If you do not respond after this time, you will enter the fallback code.
The first request tends to be slow (because of the lazy loading mechanism of Spring, which instantiates 1 class), and the response time may be longer than 1 second.
Solution:
< 1: Configure the timeout for Hystrix to 5 seconds
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
< 2: Disable the timeout for Hystrix
hystrix.command.default.execution.timeout.enabled: false
< 3: Disable the hystrix feature of feign
feign.hystrix.enabled: false
Note: Individuals recommend the first or second method
3. In FeignClient interface,
If you use @ PathVariable, you must specify its value
spring cloud feign Use Apache HttpClient
Question: 1 If Content-Type is not specified, the following exception will occur? It's puzzling here?
Caused by: java.lang.IllegalArgumentException: MIME type may not contain reserved characters
Interested friends here can study the source code
ApacheHttpClient.class
private ContentType getContentType(Request request) {
ContentType contentType = ContentType.DEFAULT_TEXT;
for (Map.Entry<String, Collection<String>> entry : request.headers().entrySet())
// It will be judged here If you do not specify Content-Type Attribute Use the default above text/plain; charset=ISO-8859-1
// The problem lies in the default contentType : Format text/plain; charset=ISO-8859-1
// Go to ContentType.create(entry.getValue().iterator().next(), request.charset()); Look in the method
if (entry.getKey().equalsIgnoreCase("Content-Type")) {
Collection values = entry.getValue();
if (values != null && !values.isEmpty()) {
contentType = ContentType.create(entry.getValue().iterator().next(), request.charset());
break;
}
}
return contentType;
}
@Override
public boolean updateUser(UserDTO user)
{
LOGGER.info("===updateUser, id = " + user.getId() + " ,name= " + user.getUsername());
return false;
}
0
Solution:
@Override
public boolean updateUser(UserDTO user)
{
LOGGER.info("===updateUser, id = " + user.getId() + " ,name= " + user.getUsername());
return false;
}
1
Note specifies: Content-Type specifies the attribute value of consumes: here the value of consumes is not explained in detail, you can study it if you are interested