SpringMVC全局异常处理+拦截器使用+参数校验

发布于:2024-06-11 ⋅ 阅读:(50) ⋅ 点赞:(0)

SpringMVC全局异常处理+拦截器使用+参数校验

1 全局异常的处理机制

1.1 异常处理两种方式

开发过程中是不可避免地会出现各种异常情况的,例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题,甚至直接导致程序崩溃。因此,在开发过程中,合理处理异常、避免异常产生、以及对异常进行有效的调试是非常重要的。

对于异常的处理,一般分为两种方式:

  • 编程式异常处理:是指在代码中显式地编写处理异常的逻辑。它通常涉及到对异常类型的检测及其处理,例如使用 try-catch 块来捕获异常,然后在 catch 块中编写特定的处理代码,或者在 finally 块中执行一些清理操作。在编程式异常处理中,开发人员需要显式地进行异常处理,异常处理代码混杂在业务代码中,导致代码可读性较差。
  • 声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过配置等方式进行统一的管理和处理。在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如 @Throws@ExceptionHandler),就可以处理特定类型的异常。相较于编程式异常处理,声明式异常处理可以使代码更加简洁、易于维护和扩展。

站在宏观角度来看待声明式事务处理:

整个项目从架构这个层面设计的异常处理的统一机制和规范。

一个项目中会包含很多个模块,各个模块需要分工完成。如果张三负责的模块按照 A 方案处理异常,李四负责的模块按照 B 方案处理异常……各个模块处理异常的思路、代码、命名细节都不一样,那么就会让整个项目非常混乱。

使用声明式异常处理,可以统一项目处理异常思路,项目更加清晰明了!

1.2 基于注解异常声明异常处理

//@ControllerAdvice   //可以返回逻辑视图  转发和重定向的!
@RestControllerAdvice   //@ResponseBody  直接返回json字符串
public class GlobalExceptionHandler {

    /**
     * 算数异常的处理
     * @return
     */
    @ExceptionHandler(ArithmeticException.class)
    public Object ArithmeticException(ArithmeticException e){
        //自定义处理异常即可  handler
        String message = e.getMessage();
        System.out.println("message = " + message);
        return message;
    }

    /**
     * 全局异常 当精确查找不到对应的异常时  就查找父异常
     */
    @ExceptionHandler(Exception.class)
    public Object ExceptionHandler(Exception e){
        //自定义处理异常即可 handler
        String message = e.getMessage();
        System.out.println("message = " + message);
        return message;
    }
}

2 拦截器的使用

2.1 拦截器的概念

拦截器 Springmvc VS 过滤器 javaWeb:

  • 相似点
    • 拦截:必须先把请求拦住,才能执行后续操作
    • 过滤:拦截器或过滤器存在的意义就是对请求进行统一处理
    • 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源
  • 不同点
    • 工作平台不同
      • 过滤器工作在 Servlet 容器中
      • 拦截器工作在 SpringMVC 的基础上
    • 拦截的范围
      • 过滤器:能够拦截到的最大范围是整个 Web 应用
      • 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求
    • IOC 容器支持
      • 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的
      • 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持

选择:

功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器。

image-20240604150349001 img

2.2 拦截器的使用

package com.atguigu.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author XXDTG
 * @version 1.0
 * @api 自定义拦截器
 * @since 2024年 月 日
 */
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 执行handler之前  调佣的拦截方法
     * 一般用于编码格式设置、登录保护、权限处理
     * @param request   请求对象
     * @param response  响应对象
     * @param handler   handler就是要调用的方法对象
     * @return  true  放行   false   拦截
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);
        return true;
    }

    /**
     * 当handler执行完毕后  prehandler return true  触发的方法    没有拦截机制了
     * 对结果处理 敏感词汇检查
     * @param request   请求
     * @param response  响应
     * @param handler   handler方法
     * @param modelAndView  返回的视图和共享域对象
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 整体处理完毕调用的方法
     * @param request
     * @param response
     * @param handler
     * @param ex    handler报错了 异常对象
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

package com.atguigu.config;

import com.atguigu.interceptor.MyInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * @author XXDTG
 * @version 1.0
 * @api 项目的配置类 controller handlerMapping handlerAdapter加入ioc容器
 * @since 2024年 月 日
 */

@EnableWebMvc   //handlerAdapter配置json转化器
@Configuration
@ComponentScan("com.atguigu")
public class MvcConfig implements WebMvcConfigurer {

    /**
     * 开启静态资源查找
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    /**
     * 拦截器
     * 如果使用了多个拦截器 则先声明的优先级高  优先级高的在外层
     * 先执行优先级高的preHandler方法 最后执行优先级高的post/after方法
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置方案一:拦截全部请求
        registry.addInterceptor(new MyInterceptor());

        //配置方案二:指定地址拦截
        /*
            * 任意一层字符串
            ** 任意多层字符串
         */
        registry.addInterceptor(new MyInterceptor())
                //   /user/** 拦截user下所有请求
                .addPathPatterns("/user/**");

        //配置方案三:排除拦截
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/data1");
    }
}

3 参数校验

image-20240604154451009

在 Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。

  1. 校验概述

    JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 标准中。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。

注解 规则
@Null 标注值必须为 null
@NotNull 标注值不可为 null
@AssertTrue 标注值必须为 true
@AssertFalse 标注值必须为 false
@Min(value) 标注值必须大于或等于 value
@Max(value) 标注值必须小于或等于 value
@DecimalMin(value) 标注值必须大于或等于 value
@DecimalMax(value) 标注值必须小于或等于 value
@Size(max,min) 标注值大小必须在 max 和 min 限定的范围内
@Digits(integer,fratction) 标注值值必须是一个数字,且必须在可接受的范围内
@Past 标注值只能用于日期型,且必须是过去的日期
@Future 标注值只能用于日期型,且必须是将来的日期
@Pattern(value) 标注值必须符合指定的正则表达式

JSR 303 只是一套标准,需要提供其实现才可以使用。Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:

注解 规则
@Email 标注值必须是格式正确的 Email 地址
@Length 标注值字符串大小必须在指定的范围内
@NotEmpty 标注值字符串不能是空字符串
@Range 标注值必须在指定的范围内

Spring 4.0 版本已经拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在SpringMVC 中,可直接通过注解驱动 @EnableWebMvc 的方式进行数据校验。Spring 的 LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在Spring容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean中。Spring本身并没有提供JSR 303的实现,所以必须将JSR 303的实现者的jar包放到类路径下。

配置 @EnableWebMvc后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Validated 注解即可让 SpringMVC 在完成数据绑定后执行数据校验的工作。

导入依赖

<!-- 校验注解 -->
<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-web-api</artifactId>
    <version>9.1.0</version>
    <scope>provided</scope>
</dependency>
        
<!-- 校验注解实现-->        
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>8.0.0.Final</version>
</dependency>
package com.atguigu.pojo;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Past;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;

import java.util.Date;

/**
 * @author XXDTG
 * @version 1.0
 * @api 参数校验
 * @since 2024年 月 日
 */
@Data
public class User {

    /**
     * name  不为null和空字符串
        字符串不为空 @NotBlank     集合不为空 @NotEmpty  包装不为空 @NotNull
     * password  长度大于6
     * age 必须 >= 1
     * email 邮箱格式的字符串
     * birthday 过去时间
     */

    @NotBlank
    private String name;

    @Length(min = 6)
    private String password;

    @Min(1)
    private int age;

    @Email
    private String email;

    @Past
    private Date birthday;
}

/**
 * @author XXDTG
 * @version 1.0
 * @api
 * @since 2024年 月 日
 */
@Controller
@RequestMapping("user")
public class UserController {

    /**
     * 步骤一:  实体类属性添加检验注解
     * 步骤二:  handler(@validated实体类 对象)
     * 细节:  param | json 校验注解都有效果
     *          json参数  - @RequestBody
     * 如果 不符合校验规则 直接向前端抛出异常
     * 接收错误绑定信息!自定义返回结果!约定:参数错误 ——》{code:400} -> 前端
     * 捕捉错误绑定错误信息
     *      1. handler(校验对象,BindingResult result) 要求:BindingResult必须紧挨着校验对象
     *      2. bindingresult获取绑定错误
     */
    @PostMapping("register")
    public Object register(@Validated @RequestBody User user, BindingResult result){
        if(result.hasErrors()){
            //有绑定错误 就不直接返回了 自己写处理代码
            Map data = new HashMap();
            data.put("code",400);
            data.put("msg","参数校验异常了");
            return data;
        }
        System.out.println("user = " + user);
        return user;
    }


}

网站公告

今日签到

点亮在社区的每一天
去签到