主要是三个处理:
1、统一用户登录权限验证;
2、统一异常处理;
3、统一数据格式返回。
目录
一、用户登录权限校验
🍅 1、使用拦截器
🎈 1.1自定义拦截器
🎈 1.2 设置自定义拦截器
🎈创建controller类,并且运行项目
🍅 2、拦截器原理
二、统一异常处理
三、统一数据返回
🍅 为什么需要统一数据返回格式
🍅 统一数据返回格式
🎈定义同已返回类型
🎈 同以数据处理
🎈业务类
一、用户登录权限校验
🍅 1、使用拦截器
可以对一部分方法进行拦截,而另一部分不拦截。
🎈 1.1自定义拦截器
/*
* 全局变量
* */
public class AppVar {
// Session key
public static final String SESSION_KEY = "SESSION KEY";
}
/*
* 自定义拦截器
* 返回true -> 表示拦截器验证成功,继续指定后续方法
* 返回false -> 表示验证失败,不会执行后续的方法了
* */
@Component
public class UserInterceptor implements HandlerInterceptor {
@Override
public 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 {
@Autowired
private UserInterceptor userInterceptor;
@Override
public 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++) {
// 获取项⽬中使⽤的拦截器 HandlerInterceptor
HandlerInterceptor interceptor = (HandlerInterceptor)this.intercep
torList.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方法
* */
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public 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类型作出单独处理:
@Autowired private ObjectMapper objectMapper; @Override public 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); }
返回结果: