This is the 16th day of my participation in the First Challenge 2022

demand

In one project, the original server used WebService to interact with the client and service, but now it is adjusted to directly use HTTP access according to needs. Only the access mode is changed, and the data access structure remains unchanged.

try

1.HandlerMethodArgumentResolver

  1. Raw data structure: In the design of the old project,dataThe data is based onGBKthebase64Encoding, in this transformation, it is hoped that the operation of decoding will also be unified processing.
public class RequestInfo {

  private static final long serialVersionUID = -3033941769749731426L;

  private String code;

  private String message;

  @APIModelProperty (" Request data (basic64)")
  private String data;
}
Copy the code
  1. The first thought was to passSpringMvcParameter processor ofHandlerMethodArgumentResolver. The interception request enters the parameter handler and implements the corresponding interception method.
// Custom parameter resolver
@Slf4j
public class RequestDateParamMethodArgumentResolver implements HandlerMethodArgumentResolver {
  	/** * Whether the custom parameter parser ** is supported@Author: xiaocainiaoya
     * @Date: 2021/06/21 22:09:57
     * @param parameter
     * @return: * * /
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return true;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        log.info("hello");
        return null; }}// Add a parameter parser to the Spring container
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public RequestDateParamMethodArgumentResolver requestDateParamMethodArgumentResolver(a){
        return new RequestDateParamMethodArgumentResolver();
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(requestDateParamMethodArgumentResolver()); }}Copy the code

3. The breakpoint is in the resolveArgument, and I want to query the corresponding parameter information according to the breakpoint after starting, so as to further improve the code. The test found that there was no breakpoint at all.

4. Although I don’t know much about the execution process of the parameter parser, I probably know that the processing of the parameter parser is executed after the Adapter finds the corresponding HandlerMethod, so according to the call stack, find the key code.

@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// There are many built-in parameter handlers, so only one of them is selected for processing.
// Even though the supportsParameter method returns true, the user-defined parameter handler takes precedence over the built-in handler.
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            if (resolver.supportsParameter(parameter)) {
                result = resolver;
                this.argumentResolverCache.put(parameter, result);
                // Get a matching argument handler and jump out
        break; }}}return result;
}
Copy the code

** Therefore: ** If you need to use the parameter handler for processing, you need to define a custom annotation and mark it in the attribute of the method. The original intention of the transformation is still to hope that the business code layer does not care about the decoding behavior. If you need to mark an annotation in each method, it is not the original intention.

2. @InitBinder

This is a fine-grained control mode. Only a single Controller is controlled. In this Controller, the parameters are intercepted for secondary data processing.

@RestController
@Slf4j
public class ArgumentResolverController {

    @InitBinder
    public void initBinder(WebDataBinder webDataBinder){
        if(webDataBinder.getTarget() instanceof User){
            User user = (User)webDataBinder.getTarget();
            log.info(user.getUsername());
            user.setPassword("updatePassword"); }}@RequestMapping("setUserInfo")
    public String setUserInfo(@RequestBody User user){
      	// The password printed here is updatePassword, which is the modified value, so the decoding can be placed in @initbinder.
        log.info("user: {},{}",user.getUsername(), user.getPassword());
        return null; }}Copy the code

3. RequestBodyAdvice

AfterBodyRead method can be used to obtain the parsed entity after the parameter parser processing, and then perform basic64 decoding according to the type of the entity.

/** * Request parameter enhancement processing **@Author: xiaocainiaoya
 * @Date: 2021/06/21 22:37:56 * * /
@Component
@ControllerAdvice("cn.com.xiaocainiaoya.controller")
public class ArgumentAdvice implements RequestBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class
       > converterType) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class
       > converterType) throws IOException {
        return inputMessage;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class
       > converterType) {
        if(body instanceof User){
            ((User) body).setPassword("12312");
        }
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class
       > converterType) {
        return null; }}Copy the code

summary

Although I did not solve this problem by using the parameter processor, I also found that I did not understand the parameter parser well during the testing process. I thought that as long as the supportsParameter method was true, I could process parameters. All I need to do to improve the code is abstract a top-level attribute interface to determine whether it is a type.

Both @initBinder and RequestBodyAdvice are desirable solutions, depending on whether you need some sort of uniform processing for parameters globally (all controllers in a package) or just for one Controller.