之前我们的文章[记一次springboot项目自定义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; } }
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; } }
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; } }
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、测试
测试示例同方法三
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; } }
本文介绍了5种修改或者填充请求参数的方法,这边有几个小细节点需注意一下,通过自定义HandlerMethodArgumentResolver这种方式,如果方法同时存在spring默认自带的HandlerMethodArgumentResolver和自定义HandlerMethodArgumentResolver,如果直接通过重写WebMvcConfigurer添加argumentResolver这种方式,则自定义HandlerMethodArgumentResolver会失效。其次通过RequestBodyAdvice这种方式只适用于方法参数加了@RequestBody 或 HttpEntity 方法参数。最后上面这几种方式,除了用来修改或者填充参数,他还可以用来做请求参数的校验,感兴趣的朋友可以自己扩展一下