springboot的四种拦截机制
当我们在某些情况下需要对客户端发送来的请求进行拦截分析的时候,就需要用到拦截机制,比如,我们需要对一个请求进行计时,又或者需要知道当前请求需要进入哪个控制器,哪一个方法,该请求的参数是什么等等场景下都需要用到拦截机制来处理。下面,我们来讲解一下SpringBoot的几种拦截方式以及如何使用它们来处理一定的场景需求。
过滤器(filter)
拦截器(interceptor)
全局异常处理器(ControllerAdvice)
切片(aspect)
如上图所示,当一个请求发送来的时候,filter在最外层,也最先拦截到请求,接下来就是interceptor,依次是ControllerAdvice(处理controller层异常)、aspect,最后才进入controller层去处理请求。相应的,当controller内部发生错误,抛出异常的时候,aspect最先接收到该异常,如果不对抛出的异常继续处理继续往外抛的话依次会抛到ControllerAdvice、interceptor、filter。
请求的顺序:从先到后经过 Filter Interceptor ControllerAdvice Aspect – Controller。
过滤器(filter)可以拦截发送请求的状态码以及信息,拦截器(interceptor)除了可以拦截filter可以拦截的,还可以得到当前请求进入了哪一个controller,以及映射到哪一个方法,切片(aspect),它具有上面的所有功能外,还可以得到当前请求的参数的值。全局异常处理器(ControllerAdvice)只是用于处理controller层抛出的异常
Springboot的Filter,HandlerInterceptor,Aspect与异常处理
不知你在Springboot应用开发中有没有遇到过这样的情况,http请求进来却没有到达指定的controller就返回响应了,这是有可能在哪里拦截了呢。
答案是Filter,HandlerInterceptor,Aspect都有可能拦截,下面简单分析一下它们几个。
要说清它们关系得加上异常处理,在 Springboot 开发中默认的异常处理有三层,分别有 ErrorPage , ErrorController , RestControllerAdvice ,一次请求的调用链路简化图如下
① DispatcherServlet 是 Spring 框架定义的 Servlet ,在 Tomcat 中作为默认的 Servlet ,是联通 Tomcat 和 Spring 的关键节点,负责分发http请求到对应的 Controller 中处理。
② 请求建立socket链接后,先经过 Tomcat 的数个 valve ,开始进入 Filter ,再到 HandlerInterceptor , Aspect ,最后才到达 Controller 的,而相应的返回路径正好相反,先从 Controller ,最后到 Filter 。
③ 当在 HandlerInterceptor 和 Aspect 发生异常时,如果我们定义了 @RestControllerAdvice 和 @ExceptionHandler ,那么会在 DispatcherServlet 处理中,把异常交给我们定义的方法来处理。如果没有定义则往上抛异常。
④ 如果没有定义 @ExceptionHandler 或者 Filter 中发生异常时,将会在 StandardHostValve中 寻找匹配的 ErrorPage ,默认情况下,会将请求交给定义了/error路径的 Controller 处理,在 Springboot 中,默认会交给 BasicErrorController 处理,
因为是在 StandardHostValve 发起的链路,所以 Filter , HandlerInterceptor , Aspect 都会重新走一遍,但是有一点不同的是,这条链路只会调用类型为error的 Filter ,所以我们自定义的 Filter 不会重新调用。
⑤ 如果前面两道防线都失守了,或者③的异常处理链路中再次发生异常,那么将会在 ErrorReportValve 中处理异常,这时候处理异常比较简单粗暴,直接读取匹配的html文件,如果没有找到html文件,就会拼接一个固定样式的html报文响应回去。
SpringBoot集成SpringSecurity - 异常处理(三)
源码地址:
当我们登录失败的时候,SpringSecurity 帮我们跳转到了 /login?error URL,奇怪的是不管是控制台还是网页上都没有打印错误信息。
这是因为首先 /login?error 是SpringSecurity 默认的失败 URL,其次如果你不自己处理这个异常,这个异常时不会被处理的。
我们先来列举下一些 SpringSecurity 中常见的异常:
SpringSecurity的异常处理是在过滤器中进行的,我们在 AbastrctAuthenticationProcessingFilter 中找到了对 Authentication 的处理:
至此 SpringSecurity 完成了异常处理,总结下流程:
– AbstractAuthenticationProcessingFilter .doFilter()
– AbstractAuthenticationProcessingFilter. unsuccessfulAuthentication()
– SimpleUrlAuthenticationFailureHandler. onAuthenticationFailure()
– SimpleUrlAuthenticationFailureHandler. saveException()
上面通过源码看着挺复杂,但真正处理起来SpringSecurity为我们提供了方便的方式,我们只需要指定错误的url,然后在该方法中对异常进行处理即可。
首先我们修改 CustomUserDetailsService loadUserByUsername() 方法的返回值:
细心的同学再完成上面功能是会发现,当我们输入的用户名不存在时,不会抛出 UserNameNotFoundException ,而是抛出 BadCredentialsException 这个异常,如果有需要区分 用户名不存在和密码错误的,可参考 。
Springboot全局异常处理
从 spring 3.2 开始,新增了 @ControllerAdvice 注解,可以用于定义@ExceptionHandler,并应用到配置了@RequestMapping 的控制器中。
如果这接口是给第三方调用那是不行的,至此大致能了解到为啥需要对异常进行全局捕获了。
原理也很简单,Spring Boot 默认提供了程序出错的结果映射路径/error。这个/error请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自客户端浏览器(浏览器通常默认自动发送请求头内容Accept:text/html)还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容。
浏览器端访问的话,任何错误Spring Boot返回的都是一个Whitelabel Error Page的错误页面,这个很不友好,所以我们可以自定义下错误页面。
这样运行的时候,请求一个不存在的页面或服务端处理发生异常时,展示的自定义错误界面。
Spring Boot提供的ErrorController是一种全局性的容错机制。此外,你还可以用@ControllerAdvice注解和@ExceptionHandler注解实现对指定异常的特殊处理。
这里介绍两种情况:
局部异常主要用到的是@ExceptionHandler注解,此注解注解到类的方法上,当此注解里定义的异常抛出时,此方法会被执行。如果@ExceptionHandler所在的类是@Controller,则此方法只作用在此类。如果@ExceptionHandler所在的类带有@ControllerAdvice注解,则此方法会作用在全局。
在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。
简单的说,进入Controller层的错误才会由@ControllerAdvice处理,拦截器抛出的错误以及访问错误地址的情况@ControllerAdvice处理不了,由SpringBoot默认的异常处理机制处理。
我们实际开发中,如果是要实现RESTful API,那么默认的JSON错误信息就不是我们想要的,这时候就需要统一一下JSON格式,所以需要封装一下。
此类在common的项目,要暴露出去给依赖的项目使用,在文件src\main\resources\META-INF\spring.factories中添加最后一行
可以被全局异常捕捉并处理成json
访问接口,如果无数据,则输出异常信息
{"data":"package id为:BZ-20200107000005 的indexpackage无记录","flag":false,"code":null,"msg":"未查到数据"}
全局异常类可以用 @RestControllerAdvice ,替代 @ControllerAdvice ,因为这里返回的主要是json格式,这样可以少写一个 @ResponseBody 。
springboot怎么取消同意的异常处理
使用ErrorController类来实现。
系统默认的错误处理类为BasicErrorController,将会显示如上的错误页面。
这里编写一个自己的错误处理类,上面默认的处理类将不会起作用。
getErrorPath()返回的路径服务器将会重定向到该路径对应的处理类,本例中为error方法。