Springmvc表述层(Controleer层)框架
五:SpringMvc其它扩展
5.1:全局异常处理机制:
5.1.1异常处理的两种机制:
1.编程式异常处理:
在代码中显示的编写处理异常的逻辑; eg:使用try-catch来捕获异常,然后再catch中编写特定的处理代码,或者再finally中执行一些清理操作;--》:即开发人员需要显示的进行异常处理,可读性较差;
2.声明式事务处理:将异常处理的逻辑从具体业务中提取出来,通过配置等方式进行统一管理;
-->:在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如 @Throws 或 @ExceptionHandler),就可以处理特定类型的异常。
5.1.2:声明式事务处理:
步骤:
1.声明一个全局异常处理类(全局异常处理器);:使用@RestControllerAdvice注解(底层是aop)
2.在里面写若干个异常处理方法handler:
在handler上通过@ExceptionHadler注解来指定对应的异常类型!!!!;
之后发生响应的异常就会自动调用全局异常处理类中对应的handler方法;
注意:@RestControllerAdvice注解:代表handler方法处理完之间返回字符串;
代码演示:
第一步:
//异常处理类/器: 只要异常发生时:会走此类下对应的handler,然后会根据@ExceptionHandler注解指定的异常
//取找对应的handler方法;
//指定的异常:可以精准查找,如果不是精准或者查找父层
@RestControllerAdvice //代表直接返回json字符串
public class GlobalExceptionHandler {
@ExceptionHandler(ArithmeticException.class) //-->指定异常类型
//之后当类中的出现异常时:就会到这个异常处理器中取找对应的异常处理信息(即handler方法)!!!
public Object ArithmeticExceptionHandler(ArithmeticException e){ //-->使用对象接收异常处理信息
//自定义处理异常逻辑 eg:返回;
String message = e.getMessage();
System.out.println("message = " + message);
return message;
}
@ExceptionHandler(Exception.class) //如果没有精准的对应类型的异常:就都走这个父异常
public Object Exceptionhandler( ArithmeticException e){
//自定义处理异常逻辑
String message = e.getMessage();
return message;
}
}
第二步:
@RestController //=@Controller + @ResponseBody
@RequestMapping("user")
public class UserController {
//handler:
@GetMapping("data")
public String data(){
//1.造一个空指针异常
String name=null;
name.toString();
return "ok";
}
@RequestMapping("data1")
public String data1(){
//2.造一个算数异常:
int i=1/0;
return "ok";
}
}
注意: @ExceptionHandler(Exception.class) //如果没有精准的对应类型的异常:就都走这个父异常!!!!
第三步:
配置:配置类+初始化类
5.2拦截器
作用类似于javaweb的过滤器:从外部整体拦截
而SpringMvc中使用的是:拦截器(HandlerInterceptor);
拦截器:拦截SpringMvc内部的流程
出现的位置:
1.调用handler之前,2.调用handler之后,3.以及整体完毕之后
一般我们都使用拦截器
使用方法/步骤:
1.创建拦截器类;
实现HandlerInterceptor接口:(里面有3个方法:一般用的最多的是:prehandler()方法)
2.将拦截器加到ioc容器:
实现WebMvcConfig接口
然后将拦截器加到注册类就完成了拦截器的注册;
代码:
//第一步:创建拦截器类,实现方法
//第二步:配置到拦配置类中:要拦截哪些路径
public class MyInterceptor implements HandlerInterceptor {
//1.preHandle()方法:前置处理方法:在执行handler之前调用的拦截方法; 有拦截机制!!!
// 使用场景:在这里面可以写:编码格式设置、登录保护、权限处理;
/*
request:请求对象
response:响应对象
handler:是我们要调用的handler方法
return true:代表放行; false:代表放行 -->我们可以通过它来控制拦截还是放行!!!
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);
System.out.println("MyInterceptor.preHandle");
return true; //ture:代表放行
}
/*
2.
postHandle():后置处理方法:当handler处理完之后触发的方法!没有拦截机制
此方法只有preHandler()为return true才触发(因为只有放行才会执行下面的方法)
//使用场景:对结果进行处理,敏感词汇检查!!!
参数:
request:请求对象
response:响应对象
handler:handler方法
modelAndView:返回的视图和共享域数据对象
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandler");
}
/*
3.
afterCompletion():整体处理完毕会执行它
参数:
request:请求对象
response:响应对象
handler:handler方法
ex:如果保错了的异常对象
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");
}
}
第二步:
配置类中配置:
//第二步:进行拦截器的注册
@Override
public void addInterceptors(InterceptorRegistry registry) { //拦截器添加的方法
//配置方案1:拦截全部请求(默认情况下:拦截器是拦截全部请求)
//registry.addInterceptor(new MyInterceptor()); //将拦截器注册到方法中
/*
之后:我们发现访问:user下的data、data1,都会走拦截器!!!!2
分析执行过程:
因为我们设置了拦截器,拦截user下的路径,之后我们访问handler方法时,就会先走拦截器,因为拦截器是true,所以放行
放行之后,就访问handler方法,执行其相关业务!!!!!!!!
*/
//配置方案2:指定地址拦截: -->: .addPathPatterns("/user/data1")
//也可以使用* :任意一层字符串; **:任意多层字符串
//eg:我们要拦截用户下面的所有请求:/uer/**
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/**");
/*
之后我们发现:我们访问user下的所有路径都会经过拦截器;
但是访问order下的路径都不会走拦截器:因为我们只设置了拦截user下的!!!
*/
//配置方案3:排除拦截;
//注意:但排除的拦截地址应该在拦截地址的内部 eg:user/data1 在user/**内部!!!!
/*
eg:我们拦截user下的请求,但是把user/data1排除掉:.excludePathPatterns("/user/data1")
*/
registry.addInterceptor(new MyInterceptor()).addPathPatterns("user/**").excludePathPatterns("/user/data1");
/*
分析:因为在拦截器中排除了data1,所以之后我们访问data1时,就不会走拦截器了;
*/
//当有两个拦截器时:哪个拦截器先执行;
/*registry.addInterceptor(new MyInterceptor());
registry.addInterceptor(new MyInterceptor1());*/
/*
*/
}
5.3参数校验:
1.参数校验概述:
参数校验:
1.背景:
前端进行访问:传来url(account、password) --> Controller层:进行接收(account、password)
--> Service层:进行参数校验:eg:非空校验、格式校验....
2.学习检验 “ 注解 ”:
jsr303系列(java提供)-->:之后我们就不需要在Controller层和Service层做校验了,直接使用“ 注解 ”即可!!!
3!!.使用/操作方法:直接在接收对象的 实体类 属性上 使用校验(注解):@NotBlank 来完成相应的校验 即可
eg:
@NotBlank
account属性
@NotBlank
password属性
2.易混总结:
@NotNull、@NotEmpty、@NotBlank 都是用于在数据校验中 检查 字段值是否为空 的注解,但是它们的用法和校验规则有所不同。
(1)@NotNull
一般用于检查包装类型是否为空
(2)@NotEmpty:
用于检查集合类型是否为空
(3)@NotBlank
用于检查字符串是否为空
3.模拟参数校验:
springmvc-high-other-05:pojo.User + controller.UserController
参数校验发步骤:
第一步:写校验信息:
a:在校验对象的属性上 使用相应的 校验注解;
b:在handler方法的形参上使用 校验对象接收即可
c:必须使用@Validated注解:才能使校验注解生效;
d:如果是json数据要加@RequestBody注解;
第二步:收集错误绑定信息
自定义返回参数绑定错误信息:
a:在校验对象后面写一个BindingResult bindingResult对象(与校验对象紧挨着)
b:BindingResult有一个方法:hasErrors():会检验绑定是否成功
如果不成功:hasErrors()就为true,我们自己写错误数据返回即可
如果成功:就正常走;
代码演示:
User实体类中的属性(User类就是要校验的对象)
/*
要求/校验:
1.name 不为null和空字符串
2.password:长度大于6
3.age 必须>=1
4.email为邮箱格式的字符串;
5.birthday:传入的是过去时间
*/
@Data
public class User {
@NotBlank //校验字符串不为空的注解
private String name;
@Length(min=6) //长度最小为6
private String password;
@Min(1)//最小值为1
private int age;
@Email //对邮箱格式进行校验
private String email;
@Past //生日必须是过去
private Date birthday;
}
UserController的handler方法:
//校验:
//接收用户数据,用户有校验注解
/*
使用校验注解的步骤:
1.为实体类的属性添加校验注解
2.handler中使用对应的实体类+@validate注解接值;
handler(@validate User user)
注意:a:加上@validate之后:校验注解才会生效;
b:接收json参数:在参数处加一个@ReauestBody
如果:不符合校验规则,会直接向前端抛出异常!
但是我们要求:接收错误绑定信息!自定义返回结果! 我们约定:参数错误,就给前端返回一个对象:code:400-->代表错误
捕捉错误绑定信息信息:操作步骤:
1.在参数后面加BindingResult result:result是绑定校验的结果,如果有它,我们可以在它内部根据结果自定义返回值
eg: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;
/*
有这个:报错之后,就不会直接给前端返回错误了,就会返回这个if里面的语句!!!
*/
}
System.out.println("user = " + user);
return user;
}