前言
之前咱们的文章记一次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
@Configurationpublic 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); }}
至于为啥这么搞,而不是通过
@Configurationpublic 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@Builderpublic class User extends MetaInfo{ private String username;}
@RestController@RequestMapping("user")public class UserController { @PostMapping("add") public User add(@RequestBody User user){ return user; }}
办法二:自定义RequestBodyAdvice
1、自定义RequestBodyAdvice
@RestControllerAdvicepublic 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@Builderpublic class Product extends MetaInfo{ private String productName;}
@RestController@RequestMapping("product")public class ProductController { @PostMapping("add") public Product add(@RequestBody Product product){ return product; }}
办法三:自定义过滤器 + 自定义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@Builderpublic class Order extends MetaInfo{ private String orderName;}
@RestController@RequestMapping("order")public class OrderController { @PostMapping("add") public Order add(@RequestBody Order order){ return order; }}
办法四:自定义拦截器 + 自定义过滤器 + 自定义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、测试
测试示例同办法三
办法五 通过AOP实现
1、编写AOP切面
@Aspect@Componentpublic 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@Builderpublic 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 办法参数。最初下面这几种形式,除了用来批改或者填充参数,他还能够用来做申请参数的校验,感兴趣的敌人能够本人扩大一下
demo链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-argument-resolver