Spring Security入门(九) 使用Spring MVC开发RESTful API-使用Filter和Interceptor拦截REST服务
一.导学
-
如果想用RESTful API记录处理时间,就需要用下面三种拦截方式了
-
RESTful API的拦截
- 过滤器(Filter)
- 拦截器(interceptor)
- 切片(Aspect)
二.过滤器(Filter)
- 编写过滤器
@Component
public class TimeFilter implements Filter {
@Override//初始化
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("time filter init");
}
@Override//过滤器逻辑
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("time filter start");
long start = new Date().getTime();
chain.doFilter(request,response);
System.out.println("time filter"+(new Date().getTime()-start));
System.out.println("time filter finish");
}
@Override//销毁
public void destroy() {
System.out.println("time filter destroy");
}
}
@GetMapping(value = "/{id:\\d+}")
@JsonView(User.UserDetailView.class)
public User getInfo(@PathVariable(name = "id") String idxxx){
// throw new UserNotExistException(idxxx);
System.out.println("进入getInfo服务");
User user = new User();
user.setUsername("playmaker");
return user;
}
- 运行结果
time filter init
...
...
...
time filter start
进入getInfo服务
time filter 11
time filter finish
- 运行结果
- 所有用户请求都用经过这个过滤器来处理
- 开发的时候可能要用第三方框架的过滤器,第三方框架的过滤器是没法改代码的,没有申明Component注解,怎么加入进来?
- 传统的方式就是去web.xml中配置,spring boot 没有这个配置文件的 但是可以通过配置类添加
@Configuration
public class WebConfig {
//和去web.xml配置标签是一样的
@Bean
public FilterRegistrationBean timeFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
TimeFilter timeFilter = new TimeFilter();
registrationBean.setFilter(timeFilter);
List<String> urls = new ArrayList<>();
urls.add("/*");//可以指定filter在那些url中起作用
registrationBean.setUrlPatterns(urls);
return registrationBean;
}
}
- 删掉TimeFilter上的注解,测试
- 效果一样的
time filter start
进入getInfo服务
time filter 262
time filter finish
-
不过,存在一个问题
-
过滤器有一些限制,比如获取不到具体是哪个控制器哪一个方法处理的,只能拿到http的请求和响应
-
过滤器是j2ee的规范,在拦截器之前,还没有进入我们的具体控制器方法的时候被调用
-
想获取这些信息,就需要用拦截器
三.拦截器(interceptor)
- 编写拦截器
@Component//光申明注解不行 需要额外配置
public class TimeInterceptor implements HandlerInterceptor {
@Override//Controller方法调用前执行,比拦截器有优势,第三个参数
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
System.out.println(((HandlerMethod)handler).getBean().getClass().getName());//雷鸣
System.out.println(((HandlerMethod)handler).getMethod().getName());//方法名
//在filter中一个方法完成逻辑的,拦截器中是两个方法 只能用request来存储数据
request.setAttribute("startTime",new Date().getTime());
return true;//决定后面是否要执行 Controller中的方法
}
@Override//Controller方法处理后调用 Controller方法抛出异常就不会调用了
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
long start = (long) request.getAttribute("startTime");
System.out.println("time interceptor 耗时:"+(new Date().getTime()-start));
}
@Override//不管抛出异常没有 都会执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
long start = (long) request.getAttribute("startTime");
System.out.println("time interceptor 耗时:"+(new Date().getTime()-start));
System.out.println("ex is "+ex);
}
}
- 添加配置
//org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter 5.0+已过时
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {//拦截器注册器
registry.addInterceptor(timeInterceptor); // 可以添加多个不同的拦截器
}
//和去web.xml配置标签是一样的
@Bean
public FilterRegistrationBean timeFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
TimeFilter timeFilter = new TimeFilter();
registrationBean.setFilter(timeFilter);
List<String> urls = new ArrayList<>();
urls.add("/*");//可以指定filter在那些url中起作用
registrationBean.setUrlPatterns(urls);
return registrationBean;
}
}
- 运行结果
time filter start
preHandle
com.playmaker.web.controller.UserController
getInfo
进入getInfo服务
postHandle
time interceptor 耗时:242
afterCompletion
time interceptor 耗时:242
ex is null
time filter 266
time filter finish
- 假如这里抛出异常了
@GetMapping(value = "/{id:\\d+}")
@JsonView(User.UserDetailView.class)
public User getInfo(@PathVariable(name = "id") String idxxx){
throw new UserNotExistException(idxxx);
// System.out.println("进入getInfo服务");
// User user = new User();
// user.setUsername("playmaker");
// return user;
}
time filter start
preHandle
com.playmaker.web.controller.UserController
getInfo
afterCompletion
time interceptor 耗时:139
ex is null
time filter 166
time filter finish
- 这里抛异常,没有posthandler了
- 但是异常仍然是空的,上次写的自定义异常处理器,把异常处理掉了,异常最终没有传递过来,自定义异常处理器是在afterCompletion之前执行的
- 改成这样就可以了
@GetMapping(value = "/{id:\\d+}")
@JsonView(User.UserDetailView.class)
public User getInfo(@PathVariable(name = "id") String idxxx){
throw new RuntimeException("user not exist");
// throw new UserNotExistException(idxxx);
// System.out.println("进入getInfo服务");
// User user = new User();
// user.setUsername("playmaker");
// return user;
}
- 这个是错误处理控制器
- 拦截器会拦截所有控制器 Controller方法调用,不仅你自己写的,Spring提供的Controller方法也会被拦截