为什么需要Spring拦截器
原生的Spring AOP实现统一拦截有两大难点,1.定义拦截的规则难写;2.在切面类中拿到HttpSession比较难。为此Spring官方提供了拦截器帮我们解决了上面痛点。
实现登入信息验证拦截器
- 实现HanderInterceptor接口
- 重写preHeadler方法,在方法中编写自己的业务代码
java
//验证登入信息的拦截器
public class LoginInterceptor implements HandlerInterceptor {
//此方法返回一个boolean,如果为true表示验证成功,继续执行后续流程
//如果是false表示验证失败,后面流程不能执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if(session != null && session.getAttribute("userinfo") != null) {
return true;
}
//身份验证失败,重定向到登入页面
response.sendRedirect("/login.html");
return false;
}
}
将拦截器添加到配置文件中,并且设置拦截规则
java
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //**表示拦截所有请求
.excludePathPatterns("/user/login") //不拦截的url地址
.excludePathPatterns("/user/reg")
.excludePathPatterns("/login.html");//登入页面不拦截,否则会报重定向多次的错误
}
}
拦截器实现原理
Spring中的拦截器是通过动态代理和环绕通知的思想实现的
给当前项目配置统一的前缀
之所以要配置统一的前缀是因为在一个服务器中有多个程序,却只有一个域名,就需要在每个程序添加url前缀才知道访问哪个程序
- 在系统的配置文件中配置
java
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/zhangsan", c ->true);//设置true启动前缀
}
- 在application.properies配置
java
server.servlet.context-path=/lisi
统一异常处理
创建一个类,并在类上标识 @ControllerAdvice
java
@ControllerAdvice
@ResponseBody
public class MyExhandler {
}
添加方法@ExeceptionHandler来订阅异常
java
/*
拦截所有空指针异常
*/
@ExceptionHandler(NullPointerException.class)
public HashMap<String, Object> nullException(NullPointerException e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", -1);
result.put("msg", "空指针异常:" + e.getMessage());
result.put("data", null);
return result;
}
@ExceptionHandler(Exception.class)
public HashMap<String, Object> exception(Exception e) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", -1);
result.put("msg", "异常:" + e.getMessage());
result.put("data", null);
return result;
}
统一数据格式返回
- 创建一个类,并添加@ControllerAdvice
- 实现ResponseBodyAdvice接口,并重写supports和beforeBodyWrite
java
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
/*
返回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) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "");
result.put("data", body);
//body是String时转换时会报错,需要特殊处理
if(body instanceof String) {
try {
return objectMapper.writeValueAsString(result);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return result;
}
}