前端开发入门到精通的在线学习网站

网站首页 > 资源文章 正文

【Spring系列教程八】Spring数据验证及Spring MVC拦截器

qiguaw 2025-03-11 20:31:37 资源文章 57 ℃ 0 评论

应用程序在执行业务逻辑前,必须通过数据校验保证接收到的输入数据是正确合法的,如代表生日的日期应该是一个过去的时间、工资的数值必须是一个正数等。一般情况下,应用程序的开发是分层的,不同层的代码由不同的开发人员负责。很多时候,同样的数据验证会出现在不同的层中,这样就会导致代码冗余,为了避免这样的情况,最好将验证逻辑和相应的域模型进行绑定,将代码验证的逻辑集中起来管理。

8.1 JSR 303

JSR-303是 Java为 Bean 数据合法性校验所提供的标准框架,它已经包含在Java EE 6.0中。JSR-303通过在 Bean属性上标注类似于@NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。可以通过 http∶/jcp.org/en/jsr/detail?id=303 了解JSR-303的详细内容。JSR-303定义了一套可标注在成员变量、属性方法上的校验注解,说明如表所示:

注解

说明

@Null

验证对象是否为null

@NotNull

验证对象是否不为null,无法检查长度为0的字符串,用于验证基本数据类型

@NotBlank

检查约束字符串是不是null,被trim的长度是否大于0,值作用于字符串,并且会去除前后空格

@AssertTrue

验证Boolean对象是否为true

@AssertFalse

验证Boolean对象是否为false

@Max(value)

验证Number和String对象是否小于等于指定的值

@Min(value)

验证Number和String对象是否大于等于指定的值

@DecimalMax(value)

被标注的值必须不大于约束中指定的最大值。这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示,小数存在精度

@DecimalMin(value)

被标注的值必须不小于约束中指定的最小值。这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示,小数存在精度

@Digits(integer,fcaction)

验证字符串是否是符合指定格式的数字,integer指定整数精度,fraction指定小数精度

@Size(min,max)

验证对象(Array、Collection、Map、String)长度是否在给定的范围之内

@Past

验证Date和Calender对象是否在当前时间之前

@Future

验证Date和Calender对象是否在当前时间之后

@Pattern

验证String对象是否符合正则表达式的规则

Hibernate Validator是JSR-303的一个参考实现,它除了支持所有标准的校验注解外,还支持如表所示的扩展注解。

@NotBlank

检查约束字符串是不是Null,被Trim的长度是否大于0。只对字符串,且去掉前后空格

@URL

验证是否是合法的url

@Email

验证是否是合法的邮件地址

@CreditCardNumber

验证是否是合法的信用卡号码

@Length(min,max)

验证字符串的长度必须在指定的范围内

@NotEmpty

检查元素是否为NULL或者EMPTY。用于Array、Collection、Map、String

@Range(min,max,message)

验证属性值必须在合适的范围内

JSR-303 的核心接口是
javax.validation.Validator,该接口根据目标对象类中所标注的校验注解进行数据校验,并得到校验结果。

8.2 SpringMVC数据验证

会默认装配一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解,即可让 Spring MVC 在完成数据绑定后执行数据校验工作。在开发中凡是用户输入的数据都需要验证,例如登录时,用户填写的用户名或者密码;注册时,用户填写的注册信息等等,如果没有SpringMVC的数据验证,那么只能通过String类提供了API或者利用正则表达式进行验证。并且这是一件很繁琐的事情,下面将示例如果使用注解进制验证数据合法性。

8.2.1 使用注解

下面将以用户登录为例进行讲解,首先添加如下依赖:



    org.hibernate.validator
    hibernate-validator
    6.2.0.Final

接下来新建VO类,使用注解

@Data
public class UserVO {
    @Max(value = 12,message = "用户名不能超过12位")
    @Min(value = 6,message = "用户名不能少于6位")
    private String username;
    @Pattern(regexp = "\\w{6,12}")
    private String password;
}

在上例的VO中,使用了@Max,@Min注解用于验证用户名长度必须在6-12位之间。验证密码时则使用了@Pattern注解,该注解可以以正则表达式来验证一个字符串是否符合标准,上例中的正则表达式表明字符串必须由6-12的数字、字母、下划线组成。

新建页面,用于填写用户名和密码:


    
${usernameMessage}

${passwordMessage}

接下来,新建Controller进行测试。

package cn.bytecollege.controller;
import cn.bytecollege.vo.UserVO;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.w3c.dom.stylesheets.LinkStyle;

import javax.validation.Valid;
import java.util.List;

@Controller
public class LoginController {
    @PostMapping("/login")
    public String login(@Valid UserVO userVO, BindingResult result, Model model){
        if(result.hasErrors()){
            if(result.hasFieldErrors("username")){
                List list = result.getFieldErrors("username");
                String msg = "";
                for (FieldError e:list) {
                    msg+=e.getDefaultMessage();
                }
                model.addAttribute("usernameMessage",msg);
            }
            if(result.hasFieldErrors("password")){
                FieldError error = result.getFieldError("password");
                model.addAttribute("passwordMessage",error.getDefaultMessage());
            }
        }
        return "login.jsp";
    }
}

部署该应用,访问登录页面,在不填写任何内容的情况下提交表单,运行结果如下图:

在上例中可以看出在Controller的方法参数User上使用了@Valid注解,这个注解用于表明对该参数进行验证。而BindingResult对象则是用于获取验证的信息,SpringMVC会将校验结果保存在该对象。该对象主要有如下几个方法:

  • FieldError getFieldError(String field)∶根据属性名获取对应的校验错误。
  • List getFieldErrors()∶获取所有的属性校验错误。
  • Object getFieldValue(String field)∶获取属性值。
  • int getErrorCount()∶获取错误数量。

8.3 拦截器

当收到请求时,DispatcherServlet 将请求交给处理器映射(HandlerMapping),让它找出对应该请求的 HandlerExecutionChain 对象。在讲解 HandlerMapping 之前,有必要认识一下这个 HandlerExecutionChain 对象。HandlerExecutionChain 顾名思义是一个执行链,它包含一个处理该请求的处理器(Handler),同时包括若干个对该请求实施拦截的拦截器(HandlerInterceptor)。当HandlerMapping 返回 HandlerExecutionChain 后,DispatcherServlet 将请求交给定义在HandlerExecutionChain 中的拦截器和处理器一并处理。HandlerExecutionChain 是负责处理请求并返回 ModelAndView 的处理执行链,其结构如图所示。请求在被 Handler 执行的前后,链中装配的 HandlerInterceptor 会实施拦截操作。


8.3.1拦截器方法

拦截器到底做了什么事情?我们通过考查拦截器的几个接口方法进行了解。

  • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Objecthandler)∶在请求到达 Handler 之前,先执行这个前置处理方法。当该方法返回false 时,请求直接返回,不会传递到链中的下一个拦截器,更不会传递到处理器链末端的 Handler中。只有返回 true 时,请求才向链中的下一个处理节点传递。
  • void postHandle(HttpServletRequest request, HttpServletResponse response, Objecthandler,ModelAndView modelAndView)∶在请求被HandlerAdapter执行后,执行这个后置处理方法。
  • void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler,Exception ex)∶在响应已经被渲染后,执行该方法。位于处理器链末端的是一个 Handler,DispatcherServlet 通过 HandlerAdapter 适配器对 Handler 进行封装,并按统一的适配器接口对 Handler 处理方法进行调用。

8.3.2 拦截器使用

下面的示例将演示拦截器的使用:

首先,定义拦截器:

package cn.bytecollege.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
    /**
     * preHandle方法是进行处理器拦截用的,该方法将在Controller方法
     * 被调用前被调用,该方法的返回值为true时拦截器才会继续往下执行,
     * 该方法的返回值为false时,整个请求就结束了。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行了preHandle方法");
        return  true;
    }
    /**
     * 该方法将在Controller方法调用后执行,方法可以对ModelAndView进行操作,
     * 该方法也只能在当前Interceptor的preHandle方法的返回值为true时才会执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行了postHandle方法");
    }
    /**
     * 该方法将在整个请求完成之后执行,作用是清理资源,
     * 该方法也只能在当前 Interceptor的preHandle方法的返回值为true时才会执行。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行了afterCompletion方法");
    }
}

定义完拦截器后,需要在配置文件中进行如下配置,才能使拦截器生效:



    
        
        
        
    

运行结果如下图:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表