SpringMVC
是一个表现层的框架,用于处理与前端的请求和响应
@ResponseBody
: 可以自动将实体类转换为JSON
REST风格
表现形式状态转换
优点
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 书写简化
异常处理
常见异常
- 框架内部抛出的异常: 因使用不合规导致
- 数据层抛出的异常: 因外部服务器故障导致(例如: 服务器访问超时)
- 业务层抛出的异常: 因业务逻辑书写错误导致(例如: 遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常: 因数据收集、校验等规则导致(例如: 不匹配的数据类型间导致异常)
等规则导致(例如: 不匹配的数据类型间导致异常) - 工具类抛出的异常: 因工具类书写不严谨不够健壮导致(例如: 必要释放的连接长期未释放)
解决策略
- 所有的异常均抛出到表现层进行处理
异常处理器
集中的、统一的处理项目中出现的异常
// 可以根据处理的异常不同,制作多个方法分别处理对应的异常
@RestControllerAdvice // 设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result doException(Exception ex) {
return new Result(403, null, "捕获到一个异常"); // code, msg
}
}
项目异常分类
- 业务异常
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常
- 项目运行过程中可预计且无法避免的异常
- 其他异常
- 编程人员未预期到的异常
处理方案
- 业务异常
- 发送对应消息传递给用户,提醒规范操作
- 系统异常
- 发送固定信息传递给用户
- 发送特定信息给运维人员
- 记录日志
- 其他异常
- 发送固定信息传递给用户
- 发送特定信息给编程人员(见识到新的异常,记录下来)
- 记录日志
编写一个自定义的异常类:
public class SystemException extends RuntimeException {
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(String message, Integer code) {
super(message);
this.code = code;
}
public SystemException(String message, Throwable cause, Integer code) {
super(message, cause);
this.code = code;
}
}
捕获异常
// 抛出异常代码
// throw new SystemException(code, "");
// code可以使用自己的异常代码类
// 可以根据处理的异常不同,制作多个方法分别处理对应的异常
@RestControllerAdvice // 设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
public class ProjectExceptionAdvice {
@ExceptionHandler(SystemException.class)
public Result doSystemException(Exception ex) {
// 记录日志
// 发送信息给运维
// 发送消息给开发人员
return new Rusult(ex.getCode(), null, ex.getMessage());
}
// 。。。
}
拦截器
Interceptor是一种动态拦截方法调用的机制
作用:
- 在指定的方法调用前后执行预先设定好的代码
- 阻止原始方法的执行
拦截器与过滤器的区别
- 归属不同:
Filter
属于Servlet
技术,Interceptor
属于SpringMVC
技术 - 拦截内容不同:
Filter
对所有访问进行拦截,Interceptor
只针对SpringMVC
的访问进行增强
使用步骤
- 制作拦截器功能类
@Component
public class ProjectInterceptor implements HandlerInterceptor {
// 表示在原始的拦截之前执行该段代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true; // 放行
}
// 表示在原始的拦截之后执行该段代码
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
// 比原始的拦截之后的代码更后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
- 制作拦截器的执行位置
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/path"); // 拦截路径可以设置多个
}
}
- 简化开发
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
// 可以配置多个拦截器,形成拦截器链,运行顺序参照拦截器的添加顺序
@Autowired
private ProjectInterceptor projectInterceptor;
@Autowired
private ProjectInterceptor2 projectInterceptor2;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置多拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
}
}
过滤器
一个例子,出现的Jwt
相关工具类见博文 工具类:
@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
@Override
public int getOrder() {
// 值越小 优先级越高
return 0;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 通过ServerWebExchange获取request和response
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 2. 校验请求url是否包含login 判断是否是登录请求
// 2.1 如果是登录请求 放行
if (request.getURI().getPath().contains("/login")) {
return chain.filter(exchange);
}
// 3. 从请求头获取token令牌
String token = request.getHeaders().getFirst("token");
// 4. 校验令牌,是否为空 是否正确
// 4.1 判断令牌是否为空
if (StringUtils.isBlank(token)) {
// 返回无权限
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// 表示请求不再向后端微服务路由
return response.setComplete();
}
// 4.2 判断令牌是否正确
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
int i = AppJwtUtil.verifyToken(claimsBody);
if (i == 1 || i == 2) {
// 说明令牌过期 返回401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
} catch (Exception e) {
// 令牌有错,可能会抛出异常,所以手动捕获
e.printStackTrace();;
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 5. 放行
return chain.filter(exchange);
}
}