主要是三个处理:
1、统一用户登录权限验证;
2、统一异常处理;
3、统一数据格式返回。
目录
一、用户登录权限校验
🍅 1、使用拦截器
🎈 1.1自定义拦截器
🎈 1.2 设置自定义拦截器
🎈创建controller类,并且运行项目
🍅 2、拦截器原理
二、统一异常处理
三、统一数据返回
🍅 为什么需要统一数据返回格式
🍅 统一数据返回格式
🎈定义同已返回类型
🎈 同以数据处理
🎈业务类
一、用户登录权限校验
🍅 1、使用拦截器
可以对一部分方法进行拦截,而另一部分不拦截。
🎈 1.1自定义拦截器
/*
* 全局变量
* */
public class AppVar {
// Session keypublic static final String SESSION_KEY = "SESSION KEY";
}/*
* 自定义拦截器
* 返回true -> 表示拦截器验证成功,继续指定后续方法
* 返回false -> 表示验证失败,不会执行后续的方法了
* */
@Component
public class UserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
// 业务方法HttpSession session = request.getSession(false);if (session!=null &&session.getAttribute(AppVar.SESSION_KEY)!=null){
// 用户已经登录return true;}return false;}
}
🎈 1.2 设置自定义拦截器
将自定义拦截器设置当前项目的配置文件中,并且设置拦截规则。
拦截器要注入到Spring中才能运行,他是伴随着Spring的启动而启动的
@Configuration
public class AppConfig implements WebMvcConfigurer {@Autowiredprivate UserInterceptor userInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(new UserInterceptor());registry.addInterceptor(userInterceptor).addPathPatterns("/**") //("/**")表示拦截所有的请求.excludePathPatterns("/user/reg")//表示过滤拦截,不拦截(/user/reg).excludePathPatterns("/user/login");//表示过滤拦截,不拦截("/user/login")}
}
🎈创建controller类,并且运行项目
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getuser")public String getUser(){System.out.println("do getUser()");return "user";}@RequestMapping("/reg")public String reg(){System.out.println("do reg()");return "reg";}@RequestMapping("/login")public String login(){System.out.println("do login()");return "login";}
}
其中:
- addPathPatterns:表示要拦截的url,“/**”表示拦截任意方法
- elcludePathPatterns:表示需要排除的URL
- 以上得拦截规则可以拦截URL,包括静态文件(图片文件、JS、CSS等),一般拦截静态文件的时候,我们可以把这些静态文件分类放在static文件中
🍅 2、拦截器原理
在使用拦截器之前:
使用拦截器之后:会在调用Controller之前进行相应的业务处理
实现原理源码分析:
所有的controller指定都会通过一个调度器DispatcherServlet来实现,
所有的请求都会执行DispatcherServlet中的doDispatcher方法,在doDispatcher会执行一系列的事件,该事件是在执行拦截器之前的,如果该事件返回false,后续就不会执行Controller。
以下是doDispatcher中的一部分代码,发现在执行controller之前都会追先执行预处理
// 调⽤预处理【重点】if (!mappedHandler.applyPreHandle(processedRequest, respon
se)) {return;}// 执⾏ Controller 中的业务mv = ha.handle(processedRequest, response, mappedHandler.g
etHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;
}
那么,关于预处理⽅法 applyPreHandle方法:从上面的源码可以看出,着和我们之前定义的拦截器相似,着就是拦截器的实现原理
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex= i++) {// 获取项⽬中使⽤的拦截器 HandlerInterceptorHandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {this.triggerAfterCompletion(request, response, (Exception)null);return false;}}return true;}
二、统一异常处理
统一异常处理:就是指常规的异常,统一处理。
统一异常处理使用的是@ControllerAdvice + @ExceptionHandler来执行的,@ControllerAdvice表示控制器通知类,@ExceptionHandler是异常处理,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。
首先认为构造一个空指针异常:
@RequestMapping("/reg")public String reg(){System.out.println("do reg()");Object obj = null;System.out.println(obj.hashCode());System.out.println();System.out.println("do reg()");return "reg";}
报错了: 这种直接给你报错的方式并不直观,所以我们可以进行统一的异常处理,返回直观的数据。
然后我们进行统一异常处理:
首先定义一个统一的返回对象:
@Data
public class ResultAjax {private int code; //状态码private String msg; //状态码的描述信息private Object data;//返回数据
}
然后定义异常管理器:
@RestControllerAdvice
public class ExceptionAdvice {@ExceptionHandler(NullPointerException.class)public ResultAjax doNullPointerException(NullPointerException e){ResultAjax resultAjax = new ResultAjax();//错误的信息使用-1描述状态码resultAjax.setCode(-1);resultAjax.setMsg("空指针异常:"+ e.getMessage());resultAjax.setData(null);return resultAjax;}
}
这时候就会返回状态的描述信息:
也可以直接使用NullPointerException的父类
@ExceptionHandler(Exception.class)public ResultAjax doException(Exception e){ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(-1);resultAjax.setMsg("空指针异常:"+ e.getMessage());resultAjax.setData(null);return resultAjax;}
三、统一数据返回
🍅 为什么需要统一数据返回格式
统一数据返回格式的优点(为什么要统一):
- 方便程序员更好的接收和解析后端数据接口返回的数据
- 降低前端程序源和后端程序员的沟通成本,按照找某个格式实现,所有接口都这样返回
- 有利于项目统一数据的维护和修改
- 有利于后端技术部分的统一规范的标准制定,不会出现稀奇古怪的返回内容
🍅 统一数据返回格式
🎈定义同已返回类型
@Data
public class ResultAjax {private int code; //状态码private String msg; //状态码的描述信息private Object data;//返回数据/** 返回成功* */public static ResultAjax success(Object data){ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(200);resultAjax.setMsg("");resultAjax.setData(data);return resultAjax;}/** 返回失败* */public static ResultAjax fail(int code,String msg){ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(code);resultAjax.setMsg(msg);resultAjax.setData(null);return resultAjax;}public static ResultAjax fail(int code,String msg,Object data){ResultAjax resultAjax = new ResultAjax();resultAjax.setCode(code);resultAjax.setMsg(msg);resultAjax.setData(null);return resultAjax;}
}
🎈 同以数据处理
统一数据处理(强制执行):
- @ControllerAdvice
- 实现ResponseBodyAdvice接口,并且重写它其中的两个方法,supports必须返回true,beforeBodyWrite方法进行重新判断和重写操作
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {/** 默认翻会true的时候* 才会执行beforeBodyWrite方法* */@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response) {//已经包装好的对象if (body instanceof ResultAjax){return body;}
// 没有包装return ResultAjax.success(body);}
}
🎈业务类
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")public ResultAjax login(){System.out.println("do login()");return ResultAjax.success("login");}@RequestMapping("/getnum")public int getNum(){return 1;}
}
其中login没有定义返回类型,getNum定义了返回类型,返回结果分别如下:
注意:
如果定义的返回值类型是String,那么会报错
@RequestMapping("/getstring")public String getString(){return "qqq";}
那么可以对String类型作出单独处理:
@Autowiredprivate ObjectMapper objectMapper;@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response) {//已经包装好的对象if (body instanceof ResultAjax){return body;} // 对字符串进行单独处理if (body instanceof String){ResultAjax resultAjax = ResultAjax.success(body);try {return objectMapper.writeValueAsString(resultAjax);} catch (JsonProcessingException e) {e.printStackTrace();}} // 没有包装return ResultAjax.success(body);}
返回结果: