SpringMVC全局异常处理+拦截器使用+参数校验
SpringMVC全局异常处理+拦截器使用+参数校验
文章目录
- SpringMVC全局异常处理+拦截器使用+参数校验
- 1 全局异常的处理机制
- 1.1 异常处理两种方式
- 1.2 基于注解异常声明异常处理
- 2 拦截器的使用
- 2.1 拦截器的概念
- 2.2 拦截器的使用
- 3 参数校验
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 的拦截器能够实现,就不使用过滤器。
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 参数校验
在 Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。
校验概述
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 地址 | |
@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;
}
}