基于Spring MVC 实现拦截器

时间:2023-03-08 17:18:39
基于Spring MVC 实现拦截器

Spring MVC 拦截器

一,具体内容:

在所有的开发之中拦截器属于一个重要的组件,可以说几乎所有的项目都会提供的概念应用,不管是Spring MVC,还是Struts 2.x都是提供有拦截器的,利用拦截器可以实现更加方便的数据验证处理。

1,认识拦截器

所谓的拦截器指的是在用户和具体操作的Action之间做了一个屏障,以保证提交到提交到Action的数据是真实有效的数据;

基于Spring MVC 实现拦截器如果要想实现拦截器的操作处理,那么必须掌握"org.springframework.web.servlet.HandlerInterceptor"该界面的定义,该界面主要有一下几个处理方法:

基于Spring MVC 实现拦截器

如果现在要想从事于具体的验证处理,那么,就应该在控制层的方法执行前进行验证,所有使用最多的方法是:preHandle

范例:定义一个拦截器

package cn.wnh.util.validate;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

public class ValidationInterceptor implements HandlerInterceptor {

Logger log = Logger.getLogger(ValidationInterceptor.class) ;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

log.info(" *** preHandle *** " + handler.getClass());

return true ;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

ModelAndView modelAndView) throws Exception {

log.info(" *** postHandle *** " + handler.getClass() + " modelAndView = " + modelAndView);

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object

handler, Exception ex)throws Exception {

log.info(" *** afterCompletion *** " + handler.getClass());

}

}

拦截器定义完成之后实际上是不能直接使用的,你还需要在applicationContext-mvc.xml档里面设置拦截器的拦截路径。

修改applicationContext-mvc    .xml档

<mvc:interceptors> <!-- 定义拦截器栈,可以定义多个拦截器-->

<mvc:interceptor> <!--定义某个具体的拦截器 -->

<:mapping path="/pages/**/*.action"/> <!-- action -->

<!-- 定义该拦截器使用的拦截器处理类,必须是界面的子类 -->

<bean class="...validate."/>

</:interceptor>

</:interceptors>

随后启动程序,正常执行某个Action。

例如:运行一下此的info方法:

@Controller

public
class {

@RequestMapping(value = "/echo/{msgParam}", produces = "text/plain;charset=UTF-8")

public
@ResponseBody String echo(@PathVariable("msgParam") String msg) {

return
"ECHO : " + msg;

}

// 定义该方法的访问路径,而后表示该方法返回的数据类型为普通的文本类型(MIME)

@RequestMapping(value="/info",produces="text/plain;charset=UTF-8")

public
@ResponseBody String info() {    // 该方法的返回值即回应的主题消息

return
"www.mldnjava.cn" ;

}

}

通过执行以上方法发现,此时有一个非常重要的类型出现了:org.springframework.web.method.HandlerMethod,这个类是拦截器中主要使用的一个程序类,此类中主要包含有如下方法:

public MethodParameter[] getMethodeParameters()

普通

取得所有提交到此方法上的参数

所有用户请求提交的参数,在此拦截器路面都使用"org.springframework.core.MethodParameter"程序类进行接收,此类有如下常用方法:、

基于Spring MVC 实现拦截器

如果需要通过拦截器进行整体的验证处理,那么必须要知道一下几点:

1,用户提交过类的参数名称以及参数的内容;

2,下雨具体的验证规则。

二,服务器端验证

服务器端的数据的接收必须要保证整个数据的合法性,例如:该接收数字的接收数字,所以按照这样的原则来讲,针对于服务器端的验证基本上可以确定的是所需要的指定数据类型的验证,而至于数据是否有效,则属于另外一个层次的验证。

  1. 常用的验证规则来讲,能够适应的验证基本上也就四种:

    int 型:\d+;

    double型:\d+(\.\d+)?;

    date 型:\d{4}-\{2}-\d{2}

  2. 如果要想进行验证,则肯定不同的A同中的不同方法可能会使用不同的规则,使用首先奥解决的是该如何去定义这个验证规则的名字(Validation.properties文件)。

    通过"HandlerMethod"类来取得所需要的一些信息:

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {

    // 需要取得HandlerMethod Action对象,这样可以取得相关Action信息

    HandlerMethod handlerMethod = (HandlerMethod) handler ;

    log.info(" *** preHandle *** beanName = " + handlerMethod.getBean().getClass().getSimpleName()

    + " methodName = " + handlerMethod.getMethod().getName());

    return true;

    }

    输出如下信息:INFO [cn.mldn.util.validate.ValidationInterceptor] - *** preHandle *** beanName = EmpAction methodName= info

    所以这个时候对于规则名称的定义为:EmpAction.info.rules。

    在拦截器里面需要拼凑出规则的key的名称:

    通过给定的Action名称以及啊哟调用的业务方法"rules"一起拼凑出要取出的验证规则,在Validations.properties中定义如下验证名称(Aciton类名称+该类方法名+rules):

    String validationKey = handlerMethod.getBean().getClass().getSimpleName() + "." +handlerMethod.getMethod().getName() + ".rules" ;

    所有规则这个属性资源信息的取出,在所有的Action父类"AbstractAction"定义资源信息的取出操作。所有EmpAction也一定存在这样的方法。所以在拦截器里面通过具体的对象进行该方法的反射调用;

  3. 当规则取得完成之后,规则的前面为参数名称,后面为验证规则,那么这个时候就可以直接拆分这个验证规则,随后进行参数的取得;

    取得全部的提交参数,需要针对给定的规则进行拆分控制

    String result [] = validationValue.split("\\|") ; //按照|拆分

    for (int x = 0 ; x < result.length ; x ++) { // 每一个规则的组成"参数名称:规则类型"

    String temp [] = result[x].split(":") ;

    String paramName = temp[0] ;

    log.info("提交参数- paramName = " + paramName + " paramValue = " +request.getParameter(paramName));

    }

    编写一个具体的数据验证工具类:ValidatorUtils.java程序类;

    package cn.wnh.util.validator;

    import java.lang.reflect.Method;

    import java.util.HashMap;

    import java.util.Map;

    import javax.servlet.http.HttpServletRequest;

    import org.apache.log4j.Logger;

    import org.springframework.web.method.HandlerMethod;

    public class ValidatorUtils {

    private static Logger log = Logger.getLogger(ValidatorUtils.class);

    /**

    *实现提交数据的验证,使用指定 Action的指定验证规则处理

    * @param request

    * @param handlerMethod

    * @return 所有的验证错误信息保存在Map集合中返回,如果没有错误,则 Map 集合的长度为0

    */

    public static Map<String, String> validate(HttpServletRequest request, HandlerMethod handlerMethod){

    // 通过给定的Action名称以及要调用的业务方法"rules"一起拼凑出要取出的验证规则,在Validations.properties中定义

    String validationKey = handlerMethod.getBean().getClass().getSimpleName() + "."+ handlerMethod.getMethod().getName() + ".rules";

    Map<String,String> errors = new HashMap<String,String>() ; //

    // log.info(" *** preHandle *** validationValue = " + validationKey);

    try {

    // key key

    AbstractAction.getValue()

    Method getValueMethod = handlerMethod.getBean().getClass().getMethod("getValue",

    String.class,

    Object[].class);

    try { // key

    // getValue() Method

    String validationValue = (String) getValueMethod.invoke(handlerMethod.getBean(),

    validationKey, null);

    if (validationValue != null) { //

    log.info(" *** preHandle *** validationValue = " + validationValue);

    //

    String result[] = validationValue.split("\\|"); //

    for (int x = 0; x < result.length; x++) { // :

    String temp[] = result[x].split(":");

    String paramName = temp [0];

    String paramRule = temp [1] ; //

    String paramValue = request.getParameter(paramName) ;

    // log.info(" paramName = " + paramName + " paramValue = " +

    request.getParameter(paramName));

    switch (paramRule) {

    case "string" : {

    if (!ValidateRuleUtil.isString(paramValue)) { //

    String msg = (String)

    getValueMethod.invoke(handlerMethod.getBean(), "validation.string.msg", null) ;

    errors.put(paramName, msg) ;

    }

    break ;

    }

    case "int" : {

    if (!ValidateRuleUtil.isInt(paramValue)) { //

    String msg = (String)

    getValueMethod.invoke(handlerMethod.getBean(), "validation.int.msg", null) ;

    errors.put(paramName, msg) ;

    }

    break ;

    }

    case "double" : {

    if (!ValidateRuleUtil.isDouble(paramValue)) { //

    String msg = (String)

    getValueMethod.invoke(handlerMethod.getBean(), "validation.double.msg", null) ;

    errors.put(paramName, msg) ;

    }

    break ;

    }

    case "date" : {

    if (!ValidateRuleUtil.isDate(paramValue)) { //

    String msg = (String)

    getValueMethod.invoke(handlerMethod.getBean(), "validation.date.msg", null) ;

    errors.put(paramName, msg) ;

    }

    break ;

    }

    case "datetime" : {

    if (!ValidateRuleUtil.isDatetime(paramValue)) { //

    String msg = (String)

    getValueMethod.invoke(handlerMethod.getBean(), "validation.datetime.msg", null) ;

    errors.put(paramName, msg) ;

    }

    break ;

    }

    case "rand" : {

    if (!ValidateRuleUtil.isRand(request,paramValue)) { //

    String msg = (String)

    getValueMethod.invoke(handlerMethod.getBean(), "validation.rand.msg", null) ;

    errors.put(paramName, msg) ;

    }

    break ;

    }

    }

    }

    }

    } catch (Exception e) {}

    } catch (Exception e) {}

    return errors ;

    }

    }

    6,、考虑到要验证的规则处理标准化,建议可以编写一个具体的ValidateRuleUtil类完成验证;

    package cn.wnh.util.validator;

    /**

    *完成的是一个个具体的验证规则的判断操作

    * @author wnh

    */

    public class ValidateRuleUtil {

    /**

    *判断是否是整数

    * @param str

    * @return

    */

    public static boolean isInt(String str) {

    if (isString(str)) { //验证数据是否为空

    return str.matches("\\d+") ;

    }

    return false ; // 数据为空返回false

    }

    /**

    * 验证是否是日期,格式为"yyyy-MM--dd HH:mm:ss"

    * @return

    */

    public static boolean isDatetime(String str) {

    if (isString(str)) {

    return str.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}") ;

    }

    return false ;

    }

    /**

    *验证是否是日期,格式为"yyyy-MM--dd HH"

    * @return

    */

    public static boolean isDate(String str) {

    if (isString(str)) {

    return str.matches("\\d{4}-\\d{2}-\\d{2}") ;

    }

    return false ;

    }

    /**

    *验证是否是小数

    * @param str

    * @return

    */

    public static boolean isDouble(String str) {

    if (isString(str)) {

    return str.matches("\\d+(\\.\\d+)?") ;

    }

    return false ;

    }

    /**

    * 如果出入的内容为null或者是空字符串,则表示错误返回 false

    * @param str

    * @return

    */

    public static boolean isString(String str) {

    if (str == null || "".equals(str)) {

    return false ;

    }

    return true ;

    }

    /**

    * 进行验证码的检测,验证码的属性名称固定为rand

    * @param request

    * @param param

    * @return

    */

    public static boolean isRand(HttpServletRequest request,String str) {

    if (isString(str)) {

    String rand = (String) request.getSession().getAttribute("rand") ;

    if (isString(rand)) {

    return rand.equalsIgnoreCase(str) ;

    }

    }

    return false ;

    }

    }

  4. 定义Messages.properties文件保存所有的错误信息:

    validation.string.msg=该数据不允许为空!

    validation.int.msg=该数据必须是整数!

    validation.double.msg=该数据必须是数字!

    validation.rand.msg=该验证码输入有误!

    validation.date.msg= (yyyy-mm-dd)!

    validation.datetime.msg= (yyyy-mm-dd hh:mm:ss)!

  5. 在拦截器里面要根据验证规则的返回结果进行执行控制;

    可以将错误页定义为两类:

    用户特定的错误页:EmpAction.add.error.page=xxx.jsp;

    公共的错误页:error.page=/errors.jsp

    error.page=/errors.jsp

    EmpAction.add.error.page=/pages/emp_add.jsp

    定义消息的读取类:

    package cn.wnh.util.resource;

    import java.lang.reflect.Method;

    import org.springframework.web.method.HandlerMethod;

    public class ResourceReadUtil {

    /**

    *读取错误页的配置消息

    * @param handlerMethod

    * @return

    */

    public static String getErrorPageValue(HandlerMethod handlerMethod) {

    String pageKey = handlerMethod.getBean().getClass().getSimpleName() + "."

    + handlerMethod.getMethod().getName() + ".error.page";

    String pageUrl = getValue(handlerMethod,pageKey) ;

    if (pageUrl == null) {

    pageUrl = getValue(handlerMethod,"error.page") ;

    }

    return pageUrl ;

    }

    /**

    *实现消息的手工配置读取

    * @param handlerMethod

    * @param msgKey

    * @return

    */

    public static String getValue(HandlerMethod handlerMethod, String msgKey) {

    try {

    Method getValueMethod = handlerMethod.getBean().getClass().getMethod("getValue",String.class,Object[].class);

    return getValueMethod.invoke(handlerMethod.getBean(), msgKey, null).toString();

    } catch (Exception e) {

    return null ;

    }

    }

    }

    在拦截器里面实现调转控制:

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

    throws Exception {

    boolean flag = true ; //默认放行

    // 需要取得HandlerMethod Action对象,这样可以取得相关的Action消息

    HandlerMethod handlerMethod = (HandlerMethod) handler ;

    // 表示具体的验证处理操作,所有的错误信息通过Map返回

    Map<String,String> errors = ValidatorUtils.validate(request, handlerMethod) ;

    if (errors.size() > 0) { //有错

    request.setAttribute("errors", errors); // 保存在Request属性范围之中

    flag = false ; //表示选择有错误,无法向下执行

    request.getRequestDispatcher(ResourceReadUtil.getErrorPageValue(handlerMethod)).forward(request,

    response);

    } else { //没有错误

    }

    return flag;

    }

    以上只能验证最基础的表单基本信息验证。如需要更多的验证还需要进一步的完善。

    二、上传文件验证

    以上已经能够实现基本的表单验证了,这样的验证实际上基本上各个的开发结构都可以实现,例如增加写一个Servlet可以实现,使用Struts 2.x也可以实现。

    但是在Spring的拦截器之中,不像Struts 2.x那样有实现好的拦截器。实际上恰恰是因为这样才让整个的开发变得异常的灵活,也就是说可以由用户增加来做检测操作。

    1. 要进行文件上传的检测,建议设置名字规则。

      在Validations.properties文件里面追加有一下的两个配置:

      #公共的上传文件验证规则

      mime.rules=image/bmp|image/jpg|image/jpeg|image/png|image/gif

      #再编写一个属于增加类的验证规则

      EmpAction.add.mime.rules=image/bmp|image/jpg|image/jpeg|image/png|image/gif

    1. 验证的所有的处理操作都一个写在ValidatorUtils类里面实现具体的上传文件验证处理;

      if (errors.size() == 0) { //之前没有错误信息,现在表示可以对上传文件类型进行验证

      //需要判断是否当前有文件上传

      MultipartResolver mr = new CommonsMultipartResolver() ; //通过它来判断对于上传文件的接收操作

      if (mr.isMultipart(request)) { //表示的是当前有上传文件

      // 需要拼凑验证规则使用的key的信息

      String mimeKey = handlerMethod.getBean().getClass().getSimpleName() + "."

      + handlerMethod.getMethod().getName() + ".mime.rules" ;

      //取得具体的验证规则信息

      String mimeValue = ResourceReadUtil.getValue(handlerMethod, mimeKey) ;

      if (mimeValue == null) { //没有消息读到,没有设置单独的验证规则

      mimeValue = ResourceReadUtil.getValue(handlerMethod, "mime.rules") ;

      }

      //进行每一个上传文件袋具体验证操作

      String mimeResult [] = mimeValue.split("\\|") ; //因为是一组规则,需要进行拆分

      MultipartRequest mreq = (MultipartRequest) request ; // 处理上传是的request

      Map<String,MultipartFile> fileMap = mreq.getFileMap() ; //取得全部的上传文件

      if (fileMap.size() > 0) { //现在有上传文件

      //需要判断每一个文件的类型

      Iterator<Map.Entry<String,MultipartFile>> iter = fileMap.entrySet().iterator() ;

      while (iter.hasNext()) { //判断每一个文件类型

      Map.Entry<String,MultipartFile> me = iter.next() ;

      if (me.getValue().getSize() > 0) { //当前的上传文件长度大于 0,表示有上传文件

      if (!ValidateRuleUtil.isMime(mimeResult,

      me.getValue().getContentType())) { //没有验证通过

      errors.put("file", ResourceReadUtil.getValue(handlerMethod,

      "validation.mime.msg")) ;

      }

      }

      }

      }

      }

      }

      3、ValidateRuleUtil类中追加是否复合于指定的mime类型的验证处理

      /**

      * 验证传入的mime类型是否复合于当前的开发要求

      * @param mimeRules整体的验证规则

      * @param mime每一个上传文件的类型

      * @return

      */

      public static boolean isMime(String mimeRules[], String mime) {

      if (isString(mime)) {

      for (int x = 0; x < mimeRules.length; x++) {

      if (mime.equals(mimeRules[x])) {

      return true;

      }

      }

      }

      return false;

      }

      不知道写得都不够明白,总的来讲完整的基于Spring MVC 设计模式的服务器验证就实现了。如果有看不明白的人欢迎回复信息,说明自己不明白的地方,本人若有空余时间定会答复!!!