前语

之前咱们的文章记一次springboot项目自定义HandlerMethodArgumentResolver不收效原因与解法末尾留了一个思考题:在咱们项目中如何优雅修正或许填充恳求参数,本期就来揭晓这个谜底

办法一:自定义HandlerMethodArgumentResolver

执行步骤:

1、自定义HandlerMethodArgumentResolver类

public class UserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    private HandlerMethodArgumentResolver handlerMethodArgumentResolver;
    public UserHandlerMethodArgumentResolver(HandlerMethodArgumentResolver handlerMethodArgumentResolver) {
        this.handlerMethodArgumentResolver = handlerMethodArgumentResolver;
    }
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestBody.class) &&
               User.class.isAssignableFrom(parameter.getParameterType());
    }
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        User user = (User) handlerMethodArgumentResolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);
        if(StringUtils.isBlank(user.getId())){
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            String id = request.getHeader("id");
            user.setId(id);
        }
        System.out.println(user);
        return user;
    }
}

2、将自定义的HandlerMethodArgumentResolver增加进行argumentResolvers

@Configuration
public class HandlerMethodArgumentResolverAutoConfiguration implements InitializingBean {
    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
    @Override
    public void afterPropertiesSet() throws Exception {
        List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
        List<HandlerMethodArgumentResolver> customArgumentResolvers = new ArrayList<>();
        for (HandlerMethodArgumentResolver argumentResolver : argumentResolvers) {
            if(argumentResolver instanceof RequestResponseBodyMethodProcessor){
                 customArgumentResolvers.add(new UserHandlerMethodArgumentResolver(argumentResolver));
            }
            customArgumentResolvers.add(argumentResolver);
        }
        requestMappingHandlerAdapter.setArgumentResolvers(customArgumentResolvers);
    }
}

至于为啥这么搞,而不是经过

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add();
    }
}

答案就在记一次springboot项目自定义HandlerMethodArgumentResolver不收效原因与解法这篇文章中

3、测验

public class MetaInfo {
    private String id;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User extends MetaInfo{
    private String username;
}

@RestController
@RequestMapping("user")
public class UserController {
    @PostMapping("add")
    public User add(@RequestBody User user){
        return user;
    }
}

聊聊springboot项目如何优雅的修改或者填充请求参数

办法二:自定义RequestBodyAdvice

1、自定义RequestBodyAdvice

@RestControllerAdvice
public class ProductRequestBodyAdvice implements RequestBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasParameterAnnotation(RequestBody.class) &&
               Product.class.isAssignableFrom(methodParameter.getParameterType());
    }
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        return inputMessage;
    }
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        Product product = (Product) body;
        if(StringUtils.isBlank(product.getId())){
            String id = inputMessage.getHeaders().getFirst("id");
            product.setId(id);
        }
        System.out.println(product);
        return product;
    }
    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }
}

2、测验

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Product extends MetaInfo{
    private String productName;
}
@RestController
@RequestMapping("product")
public class ProductController {
    @PostMapping("add")
    public Product add(@RequestBody Product product){
        return product;
    }
}

聊聊springboot项目如何优雅的修改或者填充请求参数

办法三:自定义过滤器 + 自定义HttpServletRequestWrapper

1、自定义HttpServletRequestWrapper

public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private String body;
    @SneakyThrows
    public CustomHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        //获取恳求body
        byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());
        body = new String(bodyBytes, request.getCharacterEncoding());
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
    public String getBody() {
        return this.body;
    }
    public void setBody(String body) {
        this.body = body;
    }
}

2、自定义过滤器

public class OrderFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(servletRequest instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            requestWrapper = new CustomHttpServletRequestWrapper(httpServletRequest);
            //当header的type为filter,由filter担任填充,否则由拦截器担任
            if(Constant.HEADER_VALUE_TYPE_FILTER.equalsIgnoreCase(httpServletRequest.getHeader(Constant.HEADER_KEY_TYPE))){
                System.out.println(">>>>>>>>>>> fillBodyWithId by OrderFilter");
                RequestBodyUtil.fillBodyWithId((CustomHttpServletRequestWrapper) requestWrapper);
            }
        }
        if(requestWrapper == null) {
        	//避免流读取一次就没有了,将流传递下去
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }
    @Override
    public void destroy() {
    }
}

修正恳求体中心代码

public final class RequestBodyUtil {
    private RequestBodyUtil(){}
    public static void fillBodyWithId(CustomHttpServletRequestWrapper customHttpServletRequestWrapper){
        String body = customHttpServletRequestWrapper.getBody();
        if(JSONUtil.isJson(body)){
            Order order = JSON.parseObject(body, Order.class);
            if(ObjectUtil.isNotEmpty(order) && StringUtils.isBlank(order.getId())){
                String id = ((HttpServletRequest)customHttpServletRequestWrapper.getRequest()).getHeader(Constant.HEADER_KEY_ID);
                order.setId(id);
                String newBody = JSON.toJSONString(order);
                customHttpServletRequestWrapper.setBody(newBody);
                System.out.println(">>>>>>>>>>>>> newBody----> " + newBody);
            }
        }
    }
}

3、注册filter

  @Bean
    public FilterRegistrationBean servletRegistrationBean() {
        OrderFilter orderFilter = new OrderFilter();
        FilterRegistrationBean bean = new FilterRegistrationBean<>();
        bean.setFilter(orderFilter);
        bean.setName("orderFilter");
        bean.addUrlPatterns("/order/*");
        bean.setOrder(Ordered.LOWEST_PRECEDENCE);
        return bean;
    }

4、测验

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order extends MetaInfo{
    private String orderName;
}
@RestController
@RequestMapping("order")
public class OrderController {
    @PostMapping("add")
    public Order add(@RequestBody Order order){
        return order;
    }
}

聊聊springboot项目如何优雅的修改或者填充请求参数

办法四:自定义拦截器 + 自定义过滤器 + 自定义HttpServletRequestWrapper

1、自定义HttpServletRequestWrapper

代码同办法三,他的效果在办法四首要起到修正body参数的效果

2、自定义过滤器

代码同办法三,他的效果首要解决Required request body is missing:问题

3、自定义拦截器

public class OrderHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
                if(Order.class.isAssignableFrom(methodParameter.getParameterType())){
                    if(request instanceof CustomHttpServletRequestWrapper){
                        CustomHttpServletRequestWrapper customHttpServletRequestWrapper = (CustomHttpServletRequestWrapper) request;
                        RequestBodyUtil.fillBodyWithId(customHttpServletRequestWrapper);
                    }
                }
            }
        }
        return true;
    }
}

4、配置拦截器

public class OrderHandlerInterceptorAutoConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(orderHandlerInterceptor()).addPathPatterns("/order/**");
    }
    @Bean
    @ConditionalOnMissingBean
    public OrderHandlerInterceptor orderHandlerInterceptor(){
        return new OrderHandlerInterceptor();
    }
    }

5、测验

测验示例同办法三

聊聊springboot项目如何优雅的修改或者填充请求参数

办法五 经过AOP实现

1、编写AOP切面

@Aspect
@Component
public class MemberAspect {
    /**
     *
     * @param pjp
     * @return
     *
     * @within@target:带有相应标注的一切类的恣意办法,比方@Transactional
     * @annotation:带有相应标注的恣意办法,比方@Transactional
     * @within@target针对类的注解,@annotation针对办法的注解
     *
     * @args:参数带有相应标注的恣意办法,比方@Transactiona
     */
    @SneakyThrows
    @Around(value = "@within(org.springframework.web.bind.annotation.RestController)")
    public Object around(ProceedingJoinPoint pjp){
        MethodSignature methodSignature =  (MethodSignature)pjp.getSignature();
        HandlerMethod handlerMethod = new HandlerMethod(pjp.getTarget(),methodSignature.getMethod());
        MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
        MethodParameterUtil.fillParamValueWithId(methodParameters,pjp.getArgs(), Member.class);
        Object result = pjp.proceed();
        return result;
    }
}

修正参数的中心代码

public final class MethodParameterUtil {
    private MethodParameterUtil(){}
    public static void fillParamValueWithId(MethodParameter[] methodParameters,Object[] args,Class<? extends MetaInfo> clz){
        if(ArrayUtil.isNotEmpty(methodParameters)){
            for (MethodParameter methodParameter : methodParameters) {
                if (methodParameter.getParameterType().isAssignableFrom(clz)
                        && methodParameter.hasParameterAnnotation(InjectId.class)) {
                    Object obj = args[methodParameter.getParameterIndex()];
                    if(obj instanceof MetaInfo){
                        MetaInfo metaInfo = (MetaInfo) obj;
                        if(StringUtils.isBlank(metaInfo.getId())){
                            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                            String id = servletRequestAttributes.getRequest().getHeader(Constant.HEADER_KEY_ID);
                            metaInfo.setId(id);
                            System.out.println(">>>>>>>>>>>>> newObj----> " + JSON.toJSONString(obj));
                        }
                    }
                }
            }
        }
    }
}

2、测验

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Member extends MetaInfo{
    private String memberName;
}

@RestController
@RequestMapping("member")
public class MemberController {
    @PostMapping("add")
    public Member add(@RequestBody @InjectId Member member){
        return member;
    }
}

聊聊springboot项目如何优雅的修改或者填充请求参数

总结

本文介绍了5种修正或许填充恳求参数的办法,这边有几个小细节点需注意一下,经过自定义HandlerMethodArgumentResolver这种方法,假如办法同时存在spring默许自带的HandlerMethodArgumentResolver和自定义HandlerMethodArgumentResolver,假如直接经过重写WebMvcConfigurer增加argumentResolver这种方法,则自定义HandlerMethodArgumentResolver会失效。其次经过RequestBodyAdvice这种方法只适用于办法参数加了@RequestBody 或 HttpEntity 办法参数。最终上面这几种方法,除了用来修正或许填充参数,他还能够用来做恳求参数的校验,感兴趣的朋友能够自己扩展一下

demo链接

github.com/lyb-geek/sp…