前言
之前我们的文章记一次springboot项目自定义HandlerMethodArgumentResolver不生效原因与解法末尾留了一个思考题:在我们项目中如何优雅修改或者填充请求参数,本期就来揭晓这个谜底
方法一:自定义HandlerMethodArgumentResolver
执行步骤:
代码语言:java复制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;
}
}
代码语言:java复制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);
}
}
至于为啥这么搞,而不是通过
代码语言:java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add();
}
}
答案就在记一次springboot项目自定义HandlerMethodArgumentResolver不生效原因与解法这篇文章中
代码语言:java复制3、测试
public class MetaInfo {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
代码语言:java复制@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User extends MetaInfo{
private String username;
}
代码语言:java复制@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("add")
public User add(@RequestBody User user){
return user;
}
}
方法二:自定义RequestBodyAdvice
代码语言:java复制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;
}
}
代码语言:java复制2、测试
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Product extends MetaInfo{
private String productName;
}
代码语言:java复制@RestController
@RequestMapping("product")
public class ProductController {
@PostMapping("add")
public Product add(@RequestBody Product product){
return product;
}
}
方法三:自定义过滤器 自定义HttpServletRequestWrapper
代码语言:java复制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;
}
}
代码语言:java复制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() {
}
}
修改请求体核心代码
代码语言:java复制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);
}
}
}
}
代码语言:java复制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;
}
代码语言:java复制4、测试
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order extends MetaInfo{
private String orderName;
}
代码语言:java复制@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:问题
代码语言:java复制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;
}
}
代码语言:java复制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实现
代码语言:java复制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;
}
}
修改参数的核心代码
代码语言:java复制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));
}
}
}
}
}
}
}
代码语言:java复制2、测试
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Member extends MetaInfo{
private String memberName;
}
代码语言:java复制@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