SpringMVC 扩展
1. RESTFul 风格
RESTFul是一种基于 HTTP 和标准化设计原则的软件架构风格,用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序。
要求:
-
每一个URI 代表一种资源,是名词,也就是 url中不要带动作
-
客户端使用 GET、POST、PUT、DELETE 表示操作方式的动词对服务端资源进行操作,GET获取资源,进行查询操作。POST进行插入操作,PUT进行更新操作,DELETE 进行删除操作。
使用 RESTFul 风格,url可能是相同的,但是通过 请求方式的不同可以区别不同的请求
操作 | 传统风格 | REST风格 |
---|---|---|
保存 | /crud/saveEmp | url:/crud/emp 请求方式:POST |
删除 | /crud/deleteEmp?id=1 | url:/crud/emp/1 请求方式:DELETE |
更新 | /crud/updateEmp | url:/crud/emp 请求方式:PUT |
查询 | /crud/selectEmp?id=1 | url:/crud/emp/1 请求方式:GET |
RESTFul 并不是只能用 路径传参。在某些情况下也可以使用 param传参
当查询的是一个单一的资源,传入的是一个id值,这时候可以用 路径传参,比如根据id 查询一个用户
当查询的是一个集合资源,传入的是一个范围值,这时可以用 param传参,比如分页查询或多条件模糊查询等
而POST 和 PUT 用 请求体传参即可,即json数据
2. 全局异常处理
当出现异常时,有两种解决方案:
-
编程式异常处理:在业务逻辑代码中显示地进行异常处理,用try catch
-
声明式异常处理:将业务逻辑代码与异常处理分离,通过配置进行统一的管理,互不干扰
步骤:
-
声明异常处理控制器类
定义一个类,在类上使用 @ControllerAdvice 或 @RestControllerAdvice 注解,捕捉全局异常
-
在这个类中,声明异常处理handler方法,通过 @ExceptionHandler 注解指定相应的异常
当出现异常时,就会走对应的handler方法
@ControllerAdvice 和 @RestControllerAdvice 的区别是:前者可以返回视图,可以重定向和转发。后者返回字符串
@RestController
@RequestMapping("exception")
public class ExceptionController {
@GetMapping("data")
public String data(){
String name = null;
name.toString();
return name;
}
}
上面会出现空指针异常
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NullPointerException.class)
public Object NullPointerException(NullPointerException e){
return e.getMessage();
}
}
定义了一个全局异常处理类,里面通过 @ExceptionHandler 指定了 如果出现 NullPointerException异常就会走下面的方法,在该方法中处理异常即可。
注意:需要保证 @RestControllerAdvice 被扫描到
3. 拦截器
拦截器可以在执行handler 之前和之后或者在DispatcherServlet返回时执行一些代码,加上一些功能,如登录校验等。和Filter功能类似。
拦截器和过滤器的区别:
相似点:
-
拦截:必须把请求拦住,才能执行后续操作
-
过滤:拦截器或过滤器存在的意义就是对请求进行统一处理
-
放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源
不同点:
-
工作平台不同
-
过滤器工作在Servlet容器中
-
拦截器工作在SpringMVC 的基础上
-
-
拦截的范围
-
过滤器能够拦截到的最大范围是整个web应用
-
拦截器能够拦截到的最大范围是整个SpringMVC 负责的请求
-
-
IOC 容器支持
-
想得到过滤器ioc容器需要调用专门的工具方法,spring并没有支持
-
拦截器就放在ioc容器中,可以直接从ioc容器中装配组件,可以直接得到ioc容器的支持
-
使用
-
创建一个类,实现 HandlerInterceptor 接口,重写三个方法
-
preHandler 在处理请求的目标Handler方法之前执行,返回true就是放行,返回false拦截
-
postHandler 在目标handler方法之后执行,如果handler报错则不执行
-
afterCompletion 渲染视图后执行(在最后)一定执行
-
-
将拦截器加入到 ioc容器中
在配置类里重写 addInterceptor 方法,调用 参数 InterceptorRegistry的 registry方法,传入的参数是 new
拦截器类
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
参数:request 请求对象、response 响应对象、handler 调用的对象方法、modelAndView 返回的视图和共享域对象、Exception 异常对象
@Configuration
@ComponentScan({"com.ztone.controller","com.ztone.error"})
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
必须继承 WebMvcConfigurer
上面这种方式是拦截了所有的请求,如果想要拦截指定请求可以追加调用 addPathPatterns
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/data");
}
也可以排除拦截某些请求,调用 excludePathPatterns
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/user/**").excludePathPatterns("/user/data");
}
如果有多个拦截器,那么先声明的拦截器优先级高,优先级高的在外层,也就是 优先级高的拦截器先执行 preHandler,后执行 postHandler 和 afterCompletion
4.参数校验注解
jsr303 是Java为Bean数据合法性校验提供的标准框架
注解 | 规则 |
---|---|
@Null | 标注值必须为null |
@NotNull | 标注值不可null |
@AssertTrue | 标注值必须为true |
@AssertFalse | 标注值必须为false |
@Max(value) | 标注值必须小于等于value |
@Min(value) | 标注值必须大于等于value |
@DecimalMin(value) | 标注值必须大于等于value |
@DecimalMax(value) | 标注值必须小于等于value |
@Size(max,min) | 标注值必须在max和min范围内 |
@Digits(integer,fratction) | 标注值必须是数字,并且在可接受的范围内 |
@Past | 标注值只能用于日期型,且必须是过去的日期 |
@Future | 标注值只能用于日期型,且必须是未来的日期 |
@Pattern(value) | 标注值必须符合指定的正则表达式 |
标注值必须是格式正确的Email | |
@Length | 标注值字符串大小必须在指定的范围内 |
@NotEmpty | 标注集合长度不为空 |
@Range | 标注值必须在指定的范围内 |
@NotBlank | 标注值字符串不为null,且不是空字符串 |
使用:
-
需要导入依赖
<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>
-
在实体类的属性上使用注解
@Data public class User { @NotBlank private String name; @Length(min = 6, max = 12) private String password; @Min(0) private int age; }
-
在接收参数的地方使用 @Validated
@Controller public class UserController { @GetMapping("data") public String data(@Validated @RequestBody User user){ return null; } }
当传来的参数没有通过校验,那么就会直接向前端报异常
如果想要自定义向前端的输出,可以在参数的后面紧挨着一个 参数 BindingResult
在handler中调用 该对象的 hasErrors方法,一旦报错就会返回 true
@Controller
public class UserController {
@GetMapping("data")
public String data(@Validated @RequestBody User user, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
//处理向前端的返回数据
}
return null;
}
}