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


    前言

    之前我们的文章记一次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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    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);
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

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

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        
    
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    3、测试

    public class MetaInfo {
    
        private String id;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class User extends MetaInfo{
    
        private String username;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    
    @RestController
    @RequestMapping("user")
    public class UserController {
    
    
        @PostMapping("add")
        public User add(@RequestBody User user){
            return user;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    方法二:自定义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;
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2、测试

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class Product extends MetaInfo{
    
        private String productName;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    @RestController
    @RequestMapping("product")
    public class ProductController {
    
        @PostMapping("add")
        public Product add(@RequestBody Product product){
            return product;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方法三:自定义过滤器 + 自定义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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    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() {
        }
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    修改请求体核心代码

    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);
                }
            }
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4、测试

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class Order extends MetaInfo{
    
        private String orderName;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    @RestController
    @RequestMapping("order")
    public class OrderController {
    
        @PostMapping("add")
        public Order add(@RequestBody Order order){
            return order;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方法四:自定义拦截器 + 自定义过滤器 + 自定义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;
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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();
        }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5、测试

    测试示例同方法三

    方法五 通过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;
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    修改参数的核心代码

    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));
                            }
                        }
    
    
                    }
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2、测试

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class Member extends MetaInfo{
    
        private String memberName;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    
    @RestController
    @RequestMapping("member")
    public class MemberController {
    
        @PostMapping("add")
        public Member add(@RequestBody @InjectId Member member){
            return member;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    总结

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

    demo链接

    https://github.com/lyb-geek/springboot-learning/tree/master/springboot-argument-resolver

  • 相关阅读:
    CCL 2023 电信网络诈骗案件分类评测-第一名方案
    1033 To Fill or Not to Fill
    第四章——密码学的数学引论
    HikariCP与Spring Boot的完美集成,让您的应用更高效、更可靠!
    【k8s集群部署】使用containerd运行时部署kubernetes集群(V1.27版本)
    Python 星号的妙用 —— 灵活的序列解包
    k8s containerd查看镜像
    k8s入门之Deployment(五)
    Redis跳表详解(附面试题)
    GitHub上克隆项目
  • 原文地址:https://blog.csdn.net/kingwinstar/article/details/126314307