Spring MVC 进阶 - 拦截器、异常处理、数据校验

在现代 Web 开发中,拦截器、异常处理与数据校验是确保应用健壮性和用户体验的重要环节。Spring MVC 对此提供了强大的支持。

一、 拦截器(Interceptor)

Spring MVC 提供了HandlerInterceptor接口,用于在请求处理的各个阶段执行特定的操作,如权限校验、日志记录、性能监控等。

1. 自定义拦截器

java 复制代码
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理前执行,返回 true 继续执行,返回 false 终止请求
        String token = request.getHeader("Authorization");
        if (token == null || !token.equals("valid-token")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        return true;
    }
}

解析:

• HandlerInterceptor:Spring MVC 提供的接口,在请求处理的不同阶段(如请求前、请求后、视图渲染后)执行特定逻辑。

• preHandle方法:请求处理前执行,返回true继续执行,返回false拦截请求。

• request.getHeader("Authorization"):获取请求头中的Authorization进行身份校验。

2.注册拦截器

java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/api/**");
    }
}
}

解析:

• WebMvcConfigurer:Spring 提供的接口,可以用来配置 Spring MVC 的各类组件(如拦截器、资源映射等)。

• addInterceptors方法:注册拦截器并指定要拦截的路径,addPathPatterns("/api/**")表示拦截所有/api/开头的请求。

二、全局异常处理

Spring MVC 提供了@ControllerAdvice和@ExceptionHandler注解来集中处理应用中的异常,提升代码可维护性与可读性。

1. 自定义异常类

java 复制代码
public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}

2. 统一异常处理

java 复制代码
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<String> handleCustomException(CustomException ex) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
    }
}

解析

• @RestControllerAdvice:全局异常处理类,结合@ExceptionHandler统一捕获异常并返回响应,等效于@ControllerAdvice + @ResponseBody。

• @ExceptionHandler(CustomException.class):指定捕获CustomException异常并返回相应的 HTTP 状态码(如 400)。

• ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()):返回自定义异常消息。

三、数据校验(Validation)

在 Spring MVC 中,数据校验是确保用户输入数据有效性的关键环节。如下通过实例介绍数据校验常用4类知识。

1. 实体类数据校验

使用 JSR-303 注解对实体类字段进行校验。以下是UserDTO类的示例:

java 复制代码
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @Min(value = 18, message = "年龄必须大于或等于 18")
    private int age;

    // Getters and Setters
}

常用注解:

• @NotNull:校验对象是否为null。

• @NotBlank:校验字符串是否为空(忽略空白字符)。

• @Min和@Max:校验数值类型的最小值和最大值。

• @Size:限制字符串、集合、数组等的长度或大小。

• @Pattern:校验字符串是否符合指定的正则表达式。

• @Email:校验字符串是否符合电子邮件格式。

• @Future和@Past:校验日期是否为未来或过去的日期。

2. 控制器层数据校验

在控制器层,结合@Validated和BindingResult,可以验证请求数据是否符合要求:

java 复制代码
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.Valid;
import org.springframework.validation.BindingResult;

@RestController
@RequestMapping("/users")
@Validated
public class UserController {
    @PostMapping("/create")
    public String createUser(@Valid @RequestBody UserDTO user, BindingResult result) {
        if (result.hasErrors()) {
            return result.getAllErrors().get(0).getDefaultMessage();
        }
        return "用户创建成功";
    }
}

解析

• @Validated:标注需要校验的对象。

• BindingResult:获取校验结果,判断是否有错误。

• getAllErrors().get(0).getDefaultMessage():获取第一条校验错误信息。

3. 自定义校验注解

除了Spring MVC 提供的实体类数据校验注解和 @Valid和@Validated 外,还可以通过自定义注解结合@Constraint注解,实现更加灵活的校验逻辑。

示例:创建一个校验注解,确保用户输入的age字段是一个偶数。

步骤1. 创建自定义注解

java 复制代码
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 自定义注解:必须是偶数
@Constraint(validatedBy = EvenValidator.class) // 绑定验证器
@Target({ ElementType.FIELD, ElementType.PARAMETER }) // 适用于字段和参数
@Retention(RetentionPolicy.RUNTIME) // 在运行时可见
public @interface Even {
    String message() default "必须是偶数"; // 默认错误消息
    Class<?>[] groups() default {}; // 校验分组
    Class<? extends Payload>[] payload() default {}; // 校验载荷
}

步骤2: 创建自定义验证器

自定义验证器需要实现ConstraintValidator接口,来定义校验逻辑。

java 复制代码
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class EvenValidator implements ConstraintValidator<Even, Integer> {

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        // 判断是否为偶数
        return value != null && value % 2 == 0;
    }
}

在实体类中使用@Even注解对字段进行校验。

java 复制代码
import jakarta.validation.constraints.NotNull;

public class UserDTO {

    @NotNull(message = "年龄不能为空")
    @Even(message = "年龄必须是偶数")
    private Integer age;

    // Getters and Setters
}

通过自定义校验注解,可依据需求进行灵活的输入校验。

通过结合@Constraint注解,我们可以创建更加复杂和多样化的校验规则。

4. 分组校验

分组校验指对实体类中的校验进行分组,只有在特定的场景下才会校验指定的字段。

示例场景:

在用户注册系统中,用户注册时需要校验所有字段,在更新时只需要校验部分字段。我们通过分组校验来定义这两种不同的校验场景。

示例代码:

定义分组接口

java 复制代码
public interface CreateGroup { }
public interface UpdateGroup { }

在实体类中使用分组校验

java 复制代码
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class UserDTO {

    @NotNull(groups = CreateGroup.class)  // 注册时需要校验
    private String username;

    @NotNull(groups = CreateGroup.class)  // 注册时需要校验
    private Integer age;

    @Size(min = 5, max = 20, groups = UpdateGroup.class)  // 更新时需要校验
    private String address;

    // Getters and Setters
}

在控制器中应用分组校验

java 复制代码
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/create")
    public String createUser(@Validated(CreateGroup.class) @RequestBody UserDTO user) {
        // 注册时校验
        return "用户创建成功";
    }

    @PutMapping("/update")
    public String updateUser(@Validated(UpdateGroup.class) @RequestBody UserDTO user) {
        // 更新时校验
        return "用户更新成功";
    }
}

总结

• 分组校验:允许根据不同的场景应用不同的校验规则。

• @Validated和@Valid结合分组接口使用,可以更精细地控制校验规则。

四、总结

1. 核心要点

• 拦截器机制:基于HandlerInterceptor实现请求的预处理、后处理及视图渲染后逻辑,增强 MVC 流程控制。

• 全局异常处理:使用@ControllerAdvice结合@ExceptionHandler统一管理异常,提高代码可读性和可维护性。

• 数据校验:确保表单数据的完整性和合法性。主要包括:

• 实体类校验:使用注解验证字段。

• 控制器层校验:结合@Validated和BindingResult进行数据验证。

• 自定义校验:通过自定义注解和验证器实现复杂校验。

• 分组校验:按业务需求定义校验分组。

相关推荐
zy happy1 小时前
黑马点评前端Nginx启动失败问题解决记录
java·运维·前端·spring boot·nginx·spring
有梦想的攻城狮3 小时前
spring中的BeanFactoryAware接口详解
java·后端·spring·beanfactory
AcmenSan5 小时前
深入解析 Guava Cache
java·spring·guava
有梦想的攻城狮6 小时前
Java的Filter与Spring的Interceptor的比较
java·开发语言·spring·interceptor·filter
結城6 小时前
前后端的双精度浮点数精度不一致问题解决方案,自定义Spring的消息转换器处理JSON转换
spring·json·状态模式
神秘的t10 小时前
Spring Web MVC————入门(3)
前端·后端·spring·mvc
小扳10 小时前
SpringAI 大模型应用开发篇-SpringAI 项目的新手入门知识
spring·spring cloud·ai·语言模型·架构·prompt·oneapi
蒂法就是我11 小时前
spring boot启动报错:2002 - Can‘t connect to server on ‘192.168.10.212‘ (10061)
java·spring·10061
未来的JAVA高级开发工程师12 小时前
spring的注入方式都有什么区别
java·spring
jian1105818 小时前
java spring -framework -mvc
java·spring·mvc