为什么需要Spring拦截器
原生的Spring AOP实现统一拦截有两大难点,1.定义拦截的规则难写;2.在切面类中拿到HttpSession比较难。为此Spring官方提供了拦截器帮我们解决了上面痛点。
实现登入信息验证拦截器
- 实现HanderInterceptor接口
- 重写preHeadler方法,在方法中编写自己的业务代码
//验证登入信息的拦截器
public class LoginInterceptor implements HandlerInterceptor {//此方法返回一个boolean,如果为true表示验证成功,继续执行后续流程//如果是false表示验证失败,后面流程不能执行@Overridepublic 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;}
}
将拦截器添加到配置文件中,并且设置拦截规则
@Configuration
public class AppConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //**表示拦截所有请求.excludePathPatterns("/user/login") //不拦截的url地址.excludePathPatterns("/user/reg").excludePathPatterns("/login.html");//登入页面不拦截,否则会报重定向多次的错误}
}
拦截器实现原理
Spring中的拦截器是通过动态代理和环绕通知的思想实现的
给当前项目配置统一的前缀
之所以要配置统一的前缀是因为在一个服务器中有多个程序,却只有一个域名,就需要在每个程序添加url前缀才知道访问哪个程序
- 在系统的配置文件中配置
@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix("/zhangsan", c ->true);//设置true启动前缀}
- 在application.properies配置
server.servlet.context-path=/lisi
统一异常处理
创建一个类,并在类上标识 @ControllerAdvice
@ControllerAdvice
@ResponseBody
public class MyExhandler {
}
添加方法@ExeceptionHandler来订阅异常
/*拦截所有空指针异常*/@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
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;/*返回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) {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;}
}