SpringBoot参数非空校验在网上已经有很多资料了,自己最近要实现这一个功能,大概看了下觉得没什么难度,不想在过程中还是遇到了一些问题,在此记录,希望有遇到和我一样问题的人和过路大神不吝指教。
需求是做一个全局请求参数非空校验和异常拦截,spring提供的@Validated和Hibernate提供的@Valid目前不支持请求参数为基本类型的非空判断,只能是请求参数封装为对象时,判断对象属性非空,所以要自己实现一个对基本类型的非空判断。
首先说下网上原创转载最多的一个思路:实现一个指向方法的注解,注解中创建一个String[]属性,用来存放方法中需要非空判断的参数的名称 -----> 创建AOP,切点为注解的方法,增强方法中拿到注解中的String[],然后遍历判断是否为空,如果为空则抛出一个自定义异常 -----> 实现一个全局异常处理类,捕获抛出的自定义异常,进行后续处理。
首先说下根据这个思路的实现非常简单,也很实用,只是有两个吹毛求疵的问题。第一,注解需要写成@CheckParam({param1,param2})这样的形式加在方法上,还需要手动写param1,param2这样的要进行非空判断的参数的名称,而不是像@RequestParam注解直接加在参数上就OK了。第二,@RequestParam注解本身会判断非空,一起使用时,自己的注解无效。
下面先说第一个问题,这个问题首先想到拦截器实现。
代码1:继承HandlerInterceptorAdapter ,实现拦截器。代码说明:(代码中的CheckParamNull是自定义注解,ResponseBo是自定义的json返回类)
1 public class ParameterNotBlankInterceptor extends HandlerInterceptorAdapter { 2 //在请求处理之前进行调用(Controller方法调用之前 3 @Override 4 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { 5 6 //如果不是映射到方法直接通过 7 if (!(o instanceof HandlerMethod)) { 8 return true; 9 } 10 HandlerMethod handlerMethod = (HandlerMethod) o; 11 Parameter[] methodParameters = handlerMethod.getMethod().getParameters(); 12 for (int i = 0; i< methodParameters.length; i++){ 13 if(methodParameters[i].getAnnotation(ParamNotBlank.class) != null){ 14 CheckParamNull noblank =methodParameters[i].getAnnotation(CheckParamNull.class); 15 Object obj = httpServletRequest.getParameter(methodParameters[i].getName()); 16 httpServletResponse.setCharacterEncoding("UTF-8"); 17 if (obj == null){ 18 httpServletResponse.getWriter().write(JSON.toJSONString(ResponseBo.error(noblank.message()))); 19 return false; 20 }else if(obj instanceof String && StringUtils.isBlank((String)obj)){ 21 httpServletResponse.getWriter().write(JSON.toJSONString(ResponseBo.error(noblank.message()))); 22 return false; 23 } 24 return true; 25 } 26 } 27 return true; 28 } 29 30 //请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后) 31 @Override 32 public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { 33 34 } 35 36 //在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作) 37 @Override 38 public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { 39 40 } 41 }
代码2:加入拦截器链
1 @Configuration 2 public class InterceptorConfig extends WebMvcConfigurerAdapter { 3 public void addInterceptors(InterceptorRegistry registry){ 4 registry.addInterceptor(new ParameterNotBlankInterceptor()) 5 .addPathPatterns("/**"); 6 super.addInterceptors(registry); 7 } 8 }
使用这个方法的问题来自代码1的第15行,当我们同时使用@RequestParam的时候,如果在@RequestParam(value="重新定义的请求参数名称")的value属性重新定义了请求名称,那么代码1的第15行
Object obj = httpServletRequest.getParameter(methodParameters[i].getName());拿到的就一定是null,因为methodParameters[i].getName()拿到的名称是请求方法中参数列表中的参数名,这样即便request中有值,但是由于名称不同,也就无法取到值。虽然说@RequestParam本身就会判断非空,没有必要再用自定义注解,但是保不准别人会拿来一起用,如果能保证使用自定义注解时@RequestParam不会一起出现,那么这个方法也是可行的。或者还有一种方式就是在判断自定义注代码解这里同时判断是否存在@RequestParam注解,如果有就用@RequestParam注解中的value值来request中取值,但是springweb绑定注解那么多,也不能肯定别人就会用@RequestParam,如果要判断使用那得一大串代码,果断放弃。
出现这个问题后,就在想有没有在springweb绑定注解之后工作的方法,当时想到看能不能新加入一个解析器实现org.springframework.web.method.support.HandlerMethodArgumentResolver接口,以求在springweb绑定注解的解析器工作之后执行,写完之后发现无法执行到自己的解析器。追源码看到如下内容:
1 /* 2 * Copyright 2002-2017 the