作者:~小明学编程
文章专栏:Spring框架
格言:热爱编程的,终将被编程所厚爱。
目录
前面我们说了AOP的思想,而springboot的统一功能处理就是我们AOP的实战环节目标主要有三个
- 统⼀⽤户登录权限验证;
- 统⼀数据格式返回;
- 统⼀异常处理;
Spring AOP 统一登录验证
登录功能
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String getLogin() {
return "login";
}
@RequestMapping("/reg")
public String getReg() {
return "Reg";
}
@RequestMapping("/user1")
public String getUser() {
return "user";
}
}
Spring拦截器
现在我们想要对用户是否登录进行一个验证,然后就是首先配置我们的拦截器,定义我们的拦截规则。
想要实现一个普通的拦截器的话分为三步:
实现HandlerInterceptor的接口
重写preHand的方法
在方法中实现自己的逻辑
@Component
public class LoginInterceptor implements HandlerInterceptor {
/**
* 返回 true 表示拦截判断通过,可以访问后面的接口
* 返回 false 表示拦截判断未通过,直接返回后面结果给前端
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//得到 HttpSession 对象
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("userinfo") != null) {
//表示已经登录
return true;
}
//如果未登录,然后就跳转到登陆页面
response.sendRedirect("/login.html");
return false;
}
}
将拦截器加入到系统配置中
将拦截器加入到系统配置中也是分为三个步骤:
- 实现WebMvcConfigurer接口。
- 重写addInterceptors方法。
- 确定我们要拦截哪些请求和排除哪些请求。
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginIntercept;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginIntercept)
.addPathPatterns("/**")//拦截所有的请求
.excludePathPatterns("/user/login")//排除哪些请求不拦截
.excludePathPatterns("/user/reg")
.excludePathPatterns("/**/*.html");
}
}
不加拦截器:
加了拦截器:
统一访问前缀
有以下两种方式来实现请求地址添加 api 前缀:
第一种是通过重写我们的configurePathMatch方法
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("api",c->true);
}
第二种是直接在我们的配置文件中去设置
可以看到这个时候想要访问某个地址就需要加上我们刚刚的前缀了。
统一异常处理
如下代码当我们的程序在处理逻辑的时候如果出现异常该怎么办呢?
@RequestMapping("/login")
public String getLogin() {
int a = 10/0;
return "login";
}
此时我们可以看到前端是这个样子的,这显然不是我们想要看到的,我们前端想要的只是一个json数据,所以我们就要对这些异常进行进行统一的处理。
步骤:
- 给类上面加一个 @ControllerAdvice / @RestControllerAdvice 注解,相当于是对 controller 方法的增强。
- 给方法上加一个 @ExceptionHandler 注解,但是后面要带参数,表示要处理异常的类型。然后添加异常返回的业务代码就好了。
代码:
@ControllerAdvice
@ResponseBody
public class MyExHandler {
@ExceptionHandler(ArithmeticException.class)
public HashMap<String,Object> arithmeticException(ArithmeticException e) {
HashMap<String,Object> hashMap = new HashMap<>();
hashMap.put("state",-1);
hashMap.put("msg",e.getMessage());
hashMap.put("date",null);
return hashMap;
}
}
处理多种异常:
@ExceptionHandler(Exception.class)
public HashMap<String,Object> exception(Exception e) {
HashMap<String,Object> hashMap = new HashMap<>();
hashMap.put("state",-1);
hashMap.put("msg",e.getMessage());
hashMap.put("date",null);
return hashMap;
}
我们直接写上所有异常的父类就可以处理所有异常了,但是写了子类的时候子类的优先级最高。
统一数据类型的返回
我们前后端交互的时候最重要的就是前后端返回的数据格式,这就体现到了我们统一数据类型返回的重要性了,我们需要将我们返回的数据类型给统一起来。
实现步骤:
- 添加 @ControllerAdvice 注解。
- 实现 ResponseBodyAdvice 接口,并且重写里面的方法。必须重写 里面的 supports 和 beforeBodyWrite 方法。
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
HashMap<String,Object> hashMap = new HashMap<>();
hashMap.put("code",200);
hashMap.put("msg","");
hashMap.put("date",body);
return hashMap;
}
//用来控制我们的拦截开关
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
}
controller:
@RequestMapping("/user1")
public String getUser() {
return "user";
}
@RequestMapping("/getnum")
public Integer getNum() {
return new Random().nextInt(10);
}
这个时候我们可以看到返回了我们想要的数据格式。
这个时候我们又发现了问题,报错信息是我们的hashmap在转换成String的时候出现了问题。
这是因为我们的String类型的转换方式与其它的类型不一样,String类型有着其独特的转换方式,这个时候我们需要借助其它的工具来实现。
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
HashMap<String,Object> hashMap = new HashMap<>();
hashMap.put("code",200);
hashMap.put("msg","");
hashMap.put("date",body);
if (body instanceof String) {
try {
return objectMapper.writeValueAsString(hashMap);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return hashMap;
}
//用来控制我们的拦截开关
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
}
如上述所示,我们用jakson这个工具对其类型进行一个转换就解决了。