今天在写controller时遇到好多接口都需要我去手动的校验参数,于是想到能不能搞个东西简化掉参数的校验。也想过使用“别人的轮子”但是想想还是自己玩一玩~。
首先呢,先要搞清楚思路该怎么实现这个功能。
一、选择:拦截器和过滤器的选择。拦截器和过滤器区别
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
这个老哥的博客中上边两条是决定选择拦截器来实现的原因。
二、选择了工具,那么思路呢?
首先我们需要一个拦截器,另外我们得知道哪些需要我们去拦截,并且我们得知道什么参数需要我们去校验以及发现有参数为空的怎么处理.
这里我定义了三个注解:
Category:判断参数是否为空的注解
/**
* @Classname Category
* @Description 判断参数是否为空的注解
* @Date 2019/5/30 16:27
* @Created by lky
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Category {
/**
*
* 参数名(需要校验的参数名 英文逗号隔开)
*
*/
String value();
}
CheckParamNull:标志参数不能为空
/**
* @Classname CheckParamNull 标志参数不能为空
* @Description todo
* @Date 2019/5/30 16:30
* @Created by lky
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckParamNull {
/**
* 为空后返回的信息
*/
String result();
}
CheckParamNullClazz:标志该类参与参数校验
/**
* @Classname CheckParamNullClazz 标志该类参与参数校验
* @Description todo
* @Date 2019/5/30 16:43
* @Created by lky
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckParamNullClazz {
}
这三个注解的作用分别是帮我确定参数对象需不需要校验,哪些字段需要校验,如果检验的记过为空返回值中message
内容,接下来主要是拦截器
参数校验拦截器:CheckParamsIsNullInterceptor
/**
* @Classname CheckParamsIsNullInterceptor
* @Description todo
* @Date 2019/5/30 16:08
* @Created by lky
*/
public class CheckParamsIsNullInterceptor extends AbstractHandlerInterceptor{
/**
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
* @see org.springframework.web.servlet.HandlerInterceptor#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断是不是springMVC 的请求
//如果是SpringMVC请求
outer:
if(handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
//获得方法的注解,Category注解的才进行下一步
Category methodAnnotation = handlerMethod.getMethodAnnotation(Category.class);
if(methodAnnotation==null){//没有就跳出方法体
break outer;
}
Method method = handlerMethod.getMethod();
Class<?>[] parameterTypes = method.getParameterTypes();
for(Class<?> paramter:parameterTypes){//参数类型上有需要校验的注解才进行下一步
CheckParamNullClazz checkParamNullClazz = (CheckParamNullClazz)paramter.getAnnotation(CheckParamNullClazz.class);
if(checkParamNullClazz==null){continue ;}//判断参数需不需要校验
//将需要校验的字段由Category注解中存入数组,校验对应字段是否为空
String[] splits = methodAnnotation.value().split(",");
for(String split:splits){
Field declaredField = paramter.getDeclaredField(split);
if(declaredField==null){continue ;}//如果没有该字段就终止此次循环
String parameter = request.getParameter(split);
if(parameter!=null){continue ;}//参数不为空就终止此次循环
//参数为空就获得字段的注解得到返回的内容
CheckParamNull annotation = declaredField.getAnnotation(CheckParamNull.class);
if(annotation==null){continue ;}//如果没有校验注解,则终止此次循环
String result = annotation.result();
//如果注解的result为空就自行拼接
if(result==null){
result = "参数错误:" + split + " 为空";
}
buildResponse(response, Msg.create(ResultCode.SERVICE_PARAMETER).addData("msg",result));
return false;
}
}
}
return true;
}
/**
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
* @see org.springframework.web.servlet.HandlerInterceptor#postHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, org.springframework.web.servlet.ModelAndView)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
* @see org.springframework.web.servlet.HandlerInterceptor#afterCompletion(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
拦截器的大致流程是:判断是不是SpringMvc请求,如果是通过getMethodAnnotation(Class clazz)获得调用接口是否有
Category注解,如果得到的结果为null则说明该接口不需要拦截器进行参数校验,直接跳出if(){}方法体。
如果得到的结果不为空,就说明该接口需要拦截器去进行参数校验。
通过调用HandlerMethod的getMethod()得到目标Method对象,通过Method的getParameterTypes()获得由方法的参数类型组成的Class数组,得到了方法的参数类型对参数类型数组进行遍历。
对每个参数类型调用getAnnotation(Class<A> annotationClass)获得CheckParamNullClazz注解对象,
如果得到的CheckParamNullClazz对象为空说明该参数不参与拦截器参数校验。如果得到的不为空则可以将Category注解里面的
通过,分割的参数通过split(“,”)存到数组中,对字段数组进行遍历。通过class对象的getDeclaredField(String fieldName)
方法多的Field对象。如果获得对象为空,则证明虽然接口需要对该字段进行校验但不在该参数对象(一个方法又不一定就一个参数),如果获得对象不为空则判断request中有没该参数,有就最好。没有的话获得该字段的CheckParamNull注解,获得该参数为空是需要返回的内容
//参数为空就获得字段的注解得到返回的内容
CheckParamNull annotation = declaredField.getAnnotation(CheckParamNull.class);
如果获得CheckParamNull对象为null则进行默认的返回内容,不为空获得返回信息返回。然后prdHandlder()方法返回false;
该方法返回false就不会向下掉调用目标方法,返回true则会调用目标方法。至此代码差不多了,如果你现在就迫不及待的测试
下拦截器好用不?你会发现没起作用,原因是我们还没有为拦截器指定拦截路径,拦截器压根没其作用呢。
我们只需要再<mvc:interceptors></mvc:interceptors>中新增一个拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="类路径.CheckParamsIsNullInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
我这里指定的路径是所有,你可以视情况而定。另外我的校验逻辑比较简单,你可以自己发挥,比如判断想字段如果是数字类型
不能为0