文章目录
一、全局异常处理机制
1.1 异常处理两种方式
- 编程式异常处理
- 代码中显式地编写处理异常的逻辑
- try-catch-finally
- 声明式异常处理
- 将异常处理的逻辑从具体的业务逻辑中分离出来
- 通过配置等方式进行统一的管理和处理
- 如
@Throws
或@ExceptionHandler
1.2 基于注解异常声明异常处理
- 声明异常处理控制器类
统一定义异常处理handler方法!
// 全局异常发生,会走这个类的handler
//@ControllerAdvice // 可以返回逻辑视图 转发和重定向
@RestControllerAdvice // @ResponseBody 直接返回json字符串
public class GlobalExceptionHandler {
}
- 声明异常处理hander方法
@ExceptionHandler(异常.class)
@ExceptionHandler(ArithmeticException.class)
public Object ArithmeticExceptionHandler(ArithmeticException e){
// 算术异常
String message = e.getMessage();
System.out.println("message : " + message);
return message;
}
@ExceptionHandler(Exception.class)
public Object ExceptionHandler(Exception e){
// 全局异常
String message = e.getMessage();
System.out.println("message : " + message);
return message;
}
- 普通的handler方法要使用@RequestMapping注解映射路径,发生对应的路径调用!
- 异常处理handler方法要@ExceptionHandler映射异常,发生对应的异常会调用!
- 异常处理handler方法和普通的handler方法参数接收和响应都一致!
- 配置文件扫描控制器类配置
确保异常处理控制类被扫描
<!-- 扫描controller error对应的包,将handler加入到ioc-->
@ComponentScan({"com.wake.controller","com.wake.error"})
- 结果 result :
1.3 实战
- 定义全局异常处理器非常简单,就是定义一个类,在类上加上一个注解
@RestControllerAdvice
,加上这个注解就代表我们定义了一个全局异常处理器。 - 在全局异常处理器当中,需要定义一个方法来捕获异常,在这个方法上需要加上注解
@ExceptionHandler
。通过@ExceptionHandler注解当中的value属性
来指定我们要捕获的是哪一类型的异常。
@RestControllerAdvice
public class GlobalExceptionHandler {
//处理异常
@ExceptionHandler(Exception.class) //指定能够处理的异常类型
public Result ex(Exception e){
e.printStackTrace();//打印堆栈中的异常信息
//捕获到异常之后,响应一个标准的Result
return Result.error("对不起,操作失败,请联系管理员");
}
}
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
处理异常的方法返回值会转换为json后再响应给前端
以上就是全局异常处理器的使用,主要涉及到两个注解:
@RestControllerAdvice
//表示当前类为全局异常处理器@ExceptionHandler
//指定可以捕获哪种类型的异常进行处理
二、拦截器使用
2.1 拦截器概念
使用拦截器在请求到达具体 handler 方法前,统一执行检测。
2.2 拦截器使用
- 创建拦截器
- 创建自定义拦截器类 实现
HandlerInterceptor
接口 - 实现 三个方法 [拦截前 拦截后 整体结束后]
- 创建自定义拦截器类 实现
public class MyInterceptor implements HandlerInterceptor {
// 执行handler方法前 调用拦截方法
// 编码格式设置,登录保护,权限处理
/**
* filter - doFilter
* @param request 请求对象
* @param response 响应对象
* @param 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执行完毕之后,执行此方法!此时已经没有拦截机制了!
*
* 此方法 只有当 preHandle方法 return为TRUE 放行后才会执行
*
* 可以对结果进行处理 , 敏感词检测等
* @param request 请求
* @param response 响应
* @param handler 方法
* @param modelAndView 返回的视图和共享域数据对象
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle! handler方法执行完毕!执行此方法!");
}
/**
* 整体处理完毕
* @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 {
System.out.println("MyInterceptor.afterCompletion! 整体处理完毕!结束!");
}
}
- 修改配置类 添加 拦截器
- 全局拦截
@EnableWebMvc // HandlerAdapter配置JSON转换器
@Configuration
@ComponentScan({"com.wake.controller","com.wake.error"})
public class MvcConfig implements WebMvcConfigurer {
/**
* 添加拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//这个设置是 : 拦截全部请求(默认全拦了)
registry.addInterceptor(new MyInterceptor());
}
}
2.3 拦截配置细节和源码分析
- 精准拦截
- 指定 拦截地址
/**
* 添加拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// * 任意一层字符串
// ** 任意多层字符串
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/error/**");
}
- 排除拦截
- 排除拦截的地址应该在拦截地址的内部
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/error/**").excludePathPatterns("/error/data1");
}
- 多个拦截器 先后顺序
三、 参数校验
注解 | 规则 |
---|---|
@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) | 标注值必须符合指定的正则表达式 |
注解 | 规则 |
---|---|
标注值必须是格式正确的 Email 地址 | |
@Length | 标注值字符串大小必须在指定的范围内 |
@NotEmpty | 标注值字符串不能是空字符串 |
@Range | 标注值必须在指定的范围内 |
步骤一:实体类中添加校验注解
步骤二:handler(@Validated 实体类 对象)
细节:
param | json 校验注解都有效果
json参数 - @RequestBody
- 实体类 添加注解校验
/**
* @Description:
* 1. name 不为Null 和 空字符串
* 字符串 @NotBlank
* 集合 @NotEmpty
* 包装 @NotNull
* 2. 长度大于6
* 3. age 大于 1
* 4. 邮箱
* 5. 必须是过去的时间
*/
@Data
public class User {
@NotBlank
private String name;
@Length(min = 6)
private String password;
@Min(1)
private int age;
@Email
private String email;
@Past
private Date birthday;
}
- controller handler 方法 设置
@RestController
@RequestMapping("user")
public class UserController {
/**
* 接收用户数据,用户有检验注解
* @param user
* @param result
* @return
*/
@PostMapping("register")
public Object register(@Validated @RequestBody User user, BindingResult result){
if (result.hasErrors()){
HashMap data = new HashMap();
data.put("code",400);
data.put("msg","参数校验异常!");
return data;
}
System.out.println("user : " + user);
return user;
}
}
如果不符合校验规则,会直接向前端发出异常。
所以,需要添加 BindingResult result
参数。
来接收错误绑定信息,自定义返回结果。
约定:参数错误 -> {code : 400} -> 前端
细节:
handler(校验对象,BindingResult result)
要求:BindingResult 需要紧挨着校验对象,中间插入一个参数就会失效。
结果:
- 符合校验格式:成功
- 不符合校验格式
总结
核心点 | 掌握目标 |
---|---|
springmvc框架 | 主要作用、核心组件、调用流程 |
简化参数接收 | 路径设计、参数接收、请求头接收、cookie接收 |
简化数据响应 | 模板页面、转发和重定向、JSON数据、静态资源 |
restful风格设计 | 主要作用、具体规范、请求方式和请求参数选择 |
功能扩展 | 全局异常处理、拦截器、参数校验注解 |
拦截器 Springmvc VS 过滤器 javaWeb:
- 相似点
- 拦截:必须先把请求拦住,才能执行后续操作
- 过滤:拦截器或过滤器存在的意义就是对请求进行统一处理
- 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源
- 不同点
- 工作平台不同
- 过滤器工作在 Servlet 容器中
- 拦截器工作在 SpringMVC 的基础上
- 拦截的范围
- 过滤器:能够拦截到的最大范围是整个 Web 应用
- 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求
- IOC 容器支持
- 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的
- 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持
- 工作平台不同
选择:
功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器。
多个拦截器执行顺序
- preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置顺序调用各个 preHandle() 方法。
- postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 postHandle() 方法。
- afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 afterCompletion() 方法。
@Length注解 不成功
导入hibernate依赖:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>