SpringMVC之JSR303和拦截器

简介: SpringMVC之JSR303和拦截器

前言        

       当涉及到Web应用程序的开发时,数据校验和拦截器是非常重要的功能。在SpringMVC框架中,我们可以使用JSR303数据校验和拦截器来实现这些功能。本文将详细介绍JSR303数据校验和拦截器的使用方法和原理。

一、JSR303数据校验

1.JSR303是什么

JSR303是Java规范中定义的一套用于数据校验的标准,它提供了一种简单而强大的方式来验证数据的合法性。在Java Web开发中,我们经常需要对用户提交的数据进行校验,以确保数据的有效性和安全性。JSR303提供了一套注解,我们可以通过在实体类的字段上添加这些注解来实现数据校验。

2.为什么要使用JSR303

1. 代码简洁:使用JSR 303可以将数据校验的逻辑从业务代码中分离出来,使代码更加简洁和易于维护。我们只需要在实体类的字段上添加相应的注解,就可以定义校验规则,而不需要编写大量的校验代码。

2. 统一校验规则:通过使用JSR 303,我们可以定义一套统一的校验规则,这样可以确保所有的数据校验都遵循相同的规范。这对于多个模块或团队共同开发的项目尤为重要,可以避免各自实现不一致的校验逻辑。

3. 前后端校验一致:JSR 303支持在前端和后端都进行数据校验。前端可以使用JavaScript框架如jQuery Validation等进行校验,后端可以使用Java框架如SpringMVC等进行校验。这样可以确保前后端的校验规则一致,提高系统的安全性和用户体验。

4. 可扩展性:JSR 303提供了一套标准的校验注解,但也支持自定义注解和校验器。我们可以根据项目的需求,自定义一些特定的校验规则,以满足业务的特殊需求。

3.常用注解

注解 说明
@Null 用于验证对象为null
@NotNull 用于对象不能为null,无法查检长度为0的字符串
@NotBlank 只用于String类型上,不能为null且trim()之后的size>0
@NotEmpty 用于集合类、String类不能为null,且size>0。但是带有空格的字符串校验不出来
@Size 用于对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length 用于String对象的大小必须在指定的范围内
@Pattern 用于String对象是否符合正则表达式的规则
@Email 用于String对象是否符合邮箱格式
@Min 用于Number和String对象是否大等于指定的值
@Max 用于Number和String对象是否小等于指定的值
@AssertTrue 用于Boolean对象是否为true
@AssertFalse 用于Boolean对象是否为false

显示详细信息

@Validated@Valid是Spring框架中用于数据校验的注解,它们的作用是对方法参数或方法返回值进行校验。尽管它们的功能相似,但在使用上有一些区别。

  1. 适用范围:
  • @Valid注解适用于方法参数和方法返回值的校验。
  • @Validated注解适用于方法参数的校验。
  1. 校验器的选择:
  • @Valid注解使用的是Java Bean Validation(JSR 380)规范定义的校验器,例如Hibernate Validator。
  • @Validated注解使用的是Spring框架自带的校验器,例如Spring Validator。
  1. 分组校验:
  • @Valid注解支持分组校验,可以根据不同的校验场景选择不同的校验规则。
  • @Validated注解不支持分组校验,只能使用默认的校验规则。
  1. 错误处理:
  • @Valid注解的校验错误会被封装成MethodArgumentNotValidExceptionConstraintViolationException异常,并由全局异常处理器进行处理。
  • @Validated注解的校验错误会被封装成BindExceptionConstraintViolationException异常,并由全局异常处理器进行处理。

5.入门案例

1.导入依赖
<!-- JSR303 -->
<hibernate.validator.version>6.0.7.Final</hibernate.validator.version>
<!-- JSR303 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>${hibernate.validator.version}</version>
</dependency>
2.配置校验规则
package com.ctb.model;
import lombok.ToString;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@ToString
public class User {
    @NotNull(message = "用户编号不能为空")
    private Integer id;
    @NotBlank(message = "用户名不能为空")
    private String uname;
    private String upic;
    public User(Integer id, String uname, String upic) {
        this.id = id;
        this.uname = uname;
        this.upic = upic;
    }
    public User() {
        super();
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUname() {
        return uname;
    }
    public void setUname(String uname) {
        this.uname = uname;
    }
    public String getUpic() {
        return upic;
    }
    public void setUpic(String upic) {
        this.upic = upic;
    }
}
3.编写校验方法
//    给数据添加服务端校验
    @RequestMapping("/valiAdd")
    public String valiAdd(@Validated User user, BindingResult result, HttpServletRequest req){
//        如果服务端验证不通过,有错误
        if(result.hasErrors()){
//            服务端验证了实体类的多个属性,多个属性都没有验证通过
            List<FieldError> fieldErrors = result.getFieldErrors();
            Map<String,Object> map = new HashMap<>();
            for (FieldError fieldError : fieldErrors) {
//                将多个属性的验证失败信息输送到控制台
                System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
                map.put(fieldError.getField(),fieldError.getDefaultMessage());
            }
            req.setAttribute("errorMap",map);
        }else {
            this.userBiz.insertSelective(user);
            return "redirect:list";
        }
        return "user/edit";
    }
4.编写前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>编辑信息</h1>
<form action="${pageContext.request.contextPath }/${empty b ? 'user/valiAdd' : 'user/edit'}" method="post">
    用户编号:<input type="text" name="id" value="${b.id }">
    <span style="color: red;">${errorMap.id}</span><br>
    用户名称:<input type="text" name="uname" value="${b.uname }">
    <span style="color: red;">${errorMap.uname}</span><br>
    用户头像:<input type="text" name="upic" value="${b.upic }"><br>
    <input type="submit">
</form>
</body>
</html>
5.测试

二、拦截器(interceptor)

1.什么是拦截器

       SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个 controller生命周期之内可以多次调用。

2.拦截器与过滤器的区别

什么是过滤器(Filter)

       依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等

  • 拦截器是基于Java的反射机制实现的,而过滤器是基于Servlet规范实现的。
  • 拦截器只能拦截SpringMVC的请求,而过滤器可以拦截所有的请求。
  • 拦截器可以访问控制器的方法和参数,而过滤器只能访问请求和响应对象。
  • 拦截器可以在请求处理前后进行处理,而过滤器只能在请求处理前进行处理。

3.拦截器的应用场景及作用

  • 权限验证:拦截器可以检查用户是否具有访问某个资源的权限。
  • 日志记录:拦截器可以记录请求的详细信息,例如请求路径、请求参数等。
  • 性能监控:拦截器可以统计请求的处理时间,用于性能监控和优化。
  • 异常处理:拦截器可以捕获控制器处理过程中的异常,并进行相应的处理。

拦截器的作用是在请求到达控制器之前进行预处理,例如验证用户权限、记录日志等;在控制器处理完请求后进行后处理,例如记录响应日志、处理异常等。

4.快速入门

  • 创建拦截器

首先,创建一个实现HandlerInterceptor接口的拦截器类。该接口定义了三个方法,分别是preHandle、postHandle和afterCompletion,用于在请求处理前、请求处理后以及请求完成后进行拦截和处理。

package com.ctb.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class OneInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【OneInterceptor】:preHandle...");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("【OneInterceptor】:postHandle...");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("【OneInterceptor】:afterCompletion...");
    }
}
  • 配置拦截器

在SpringMVC的配置文件中,配置拦截器的Bean,并指定拦截的路径。

<!-- 配置拦截器-->
    <mvc:interceptors>
        <bean class="com.ctb.interceptor.OneInterceptor"></bean>
    </mvc:interceptors>
  • 测试

在运行测试时会发现它们的运行顺序为

preHandle --> postHandle --> afterCompletion

注意:拦截器会根据preHandle()方法返回值进行拦截判断,返回了一个true值。这个返回值表示该拦截器已经处理了当前的请求,并且可以继续向下传递请求。如果返回false,则表示该拦截器不处理当前请求,请求将被终止。

5.拦截器工作原理

  • preHandle:用于对拦截到的请求进行预处理,方法接收布尔(true,false)类型的返回值,返回true:放行,false:不放行。

执行时机:在处理器方法执行前执行

  • 方法参数
参数 说明
request 请求对象
response 响应对象
handler 拦截到的方法处理

  • postHandle:用于对拦截到的请求进行后处理,可以在方法中对模型数据和视图进行修改

执行时机:在处理器的方法执行后,视图渲染之前

  • 方法参数
参数 说明
request 请求对象
response 响应对象
handler 拦截到的处理器方法
ModelAndView 处理器方法返回的模型和视图对象,可以在方法中修改模型和视图

  • afterCompletion:用于在整个流程完成之后进行最后的处理,如果请求流程中有异常,可以在方法中获取对象

执行时机:视图渲染完成后(整个流程结束之后)

  • 方法参数
参数 说明
request 请求参数
response 响应对象
handler 拦截到的处理器方法
ex 异常对象
  •  

6.拦截器链

       如果多个拦截器能够对相同的请求进行拦截,则多个拦截器会形成一个拦截器链,主要理解拦截器链中各个拦截器的执行顺序。拦截器链中多个拦截器的执行顺序,根拦截器的配置顺序有关,先配置的先执行。

  • 创建拦截器
package com.ctb.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TwoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【TwoInterceptor】:preHandle...");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("【TwoInterceptor】:postHandle...");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("【TwoInterceptor】:afterCompletion...");
    }
}
  • 配置拦截器
<!-- 配置拦截器-->
    <mvc:interceptors>
        <!--2) 多拦截器(拦截器链)-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.ctb.interceptor.OneInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/user/**"/>
            <bean class="com.ctb.interceptor.TwoInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
  • 测试

7.拦截器登录权限案例

  • 创建拦截器
package com.ctb.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【implements】:preHandle...");
        StringBuffer url = request.getRequestURL();
        if (url.indexOf("/login") > 0 || url.indexOf("/logout") > 0){
            //        如果是 登录、退出 中的一种
            return true;
        }
//            代表不是登录,也不是退出
//            除了登录、退出,其他操作都需要判断是否 session 登录成功过
        String uname = (String) request.getSession().getAttribute("uname");
        if (uname == null || "".equals(uname)){
            response.sendRedirect("/page/login");
            return false;
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}
  • 配置拦截器
<!-- 配置拦截器-->
    <mvc:interceptors>
        <bean class="com.ctb.interceptor.LoginInterceptor"></bean>
    </mvc:interceptors>
  • 编写controller层处理登录的方法
package com.ctb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
 * @author 彪
 * @remark
 * @create  2023
 */
@Controller
public class LoginController {
    @RequestMapping("/login")
    public String login(HttpServletRequest req){
        String uname = req.getParameter("uname");
        HttpSession session = req.getSession();
        if ("ctb".equals(uname)){
            session.setAttribute("uname",uname);
        }
        return "redirect:/user/list";
    }
    @RequestMapping("/logout")
    public String logout(HttpServletRequest req){
        req.getSession().invalidate();
        return "redirect:/user/list";
    }
}
  • 编写前端页面
<%--
  Created by IntelliJ IDEA.
  User: 86155
  Date: 2023/9/13
  Time: 6:05
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form action="/login" method="post">
    用户:<input name="uname">
    <input type="submit">
</form>
</body>
</html>
  • 测试

登录

退出


相关文章
|
7月前
|
前端开发 Java 数据库连接
SpringMVC系列(五)之JSR303和拦截器
SpringMVC系列(五)之JSR303和拦截器
|
7月前
|
缓存 监控 Java
深入了解Spring中的JSR 303验证和拦截器
深入了解Spring中的JSR 303验证和拦截器
43 0
|
7月前
|
前端开发 Java Spring
56SpringMVC - 拦截器
56SpringMVC - 拦截器
25 0
|
6月前
|
监控 前端开发 Java
SpringMVC之JSR303使用及拦截器使用(带你探索SpringMVC的新领域)
SpringMVC之JSR303使用及拦截器使用(带你探索SpringMVC的新领域)
47 0
|
6月前
|
缓存 前端开发 Java
【SpringMVC】JSR 303与拦截器注解使用
【SpringMVC】JSR 303与拦截器注解使用
45 0
|
5月前
|
存储 缓存 前端开发
SpringMVC之JSR303和拦截器
SpringMVC之JSR303和拦截器
|
5月前
|
数据采集 监控 安全
【SpringMVC】JSR303与拦截器的使用
【SpringMVC】JSR303与拦截器的使用
48 0
|
5月前
|
监控 前端开发 Java
Springmvc之JSR303和拦截器
Springmvc之JSR303和拦截器
33 0
|
6天前
|
缓存 前端开发 Java
JSR303与拦截器的使用
JSR303与拦截器的使用
31 0
|
6月前
|
监控 前端开发 Java
SpringMVC中的JSR303与拦截器的使用
SpringMVC中的JSR303与拦截器的使用
36 0