您好,登錄后才能下訂單哦!
這篇文章主要講解了Spring boot如何集中異常處理,內容清晰明了,對此有興趣的小伙伴可以學習一下,相信大家閱讀完之后會有幫助。
方式一:ExceptionHandle
定義自己的異常類型,根據不同類型做不同處理,比如我定義的MyException:
public class MyException extends RuntimeException { public MyException(String msg) { super(msg); } }
然后通過MyExceptionHandle處理該異常,需要注意的是異常不能在filter中拋出,拋出也沒法捕獲
@RestControllerAdvice public class MyExceptionHandle { @ExceptionHandler(MyException.class) public Result exceptionHandle(MyException e) { return Result.getFailed( "system error:MyException" + e.getMessage()); } }
在controller、service以及攔截器的預處理方法中都可以完美捕獲,這里特殊說下攔截器:
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { //throw new MyException("攔截器錯誤:MyInterceptor"); // 這里的異常會完美捕獲,并返回 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { throw new MyException("攔截器錯誤:MyInterceptor"); /** 能捕獲異常信息并返回給客戶端,但并不會覆蓋已經請求成功的返回結果,但會包含在返回結果中,比如我的返回結果: {"code":1,"success":true,"msg":"請求成功","result":true}{"code":0,"success":false,"msg":"system error:MyException攔截器錯誤:MyInterceptor","result":null} */ } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { throw new MyException("攔截器錯誤:MyInterceptor"); // 這里拋出的異常并不能被捕獲,會直接在后臺拋出,相當于回掉函數,請求結果已經返回 } }
上面的解釋已經夠清楚了,就不做過多說明了。剛剛我們說了,上面的這種方式,對于filter是不起作用的,下面我們說的這種方式,主要就是針對filter來說的
方式二:ExceptionFilter
定義一個controller,請求路徑可以自己指定,比如/error/exthrow:
@Controller public class ExceptionController { @RequestMapping("/error/exthrow") public void rethrow(HttpServletRequest request) throws Exception { throw ((Exception) request.getAttribute("filter.error")); } }
再定義一個異常攔截器,在需要拋出異常的攔截器中直接拋出異常,然后在異常攔截器中try-catch,發生異常時直接轉發至前面定義的異常controller,這里需要注意的是,如果你的filter是實現Filter或者繼承OncePerRequestFilter,那你不需要任何處理,直接request.setAttribute("filter.error", e)就可以了。
由于我把自己的filter交給shiro管理,而且是繼承BasicHttpAuthenticationFilter的,不知到什么原因,直接catch到的異常類型是ServletException,為了拿到真正的異常信息,我需要通過getCause()方法獲取filter中拋出的異常。因為controller拋出的異常最后還是會交給我們定義的MyExceptionHandle去處理,如果獲取到的異常不是我們自定義的異常或者他的子類的話,就會返回500錯誤(在這個示例前,我以為所有的filter都是這樣的,后來實踐后發現并不是這樣😂)。
@Component public class ExceptionFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(request, response); } catch (Exception e) { request.setAttribute("filter.error", e); //將異常分發到/error/exthrow控制器 request.getRequestDispatcher("/error/exthrow").forward(request, response); } } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }
我的filter:
實現Filter接口:
public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { throw new MyException("MyFilter過濾器拋出異常"); //filterChain.doFilter(servletRequest, servletResponse); } }
filter2繼承OncePerRequestFilter:
public class MyFilter2 extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { throw new MyException("MyFilter2異常"); } }
filter配置類:
@Bean public FilterRegistrationBean myFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new MyFilter()); registration.setName("myFilter"); registration.addUrlPatterns("/*"); //此處盡量小,要比其他Filter靠前 registration.setOrder(1); return registration; } @Bean public FilterRegistrationBean myFilter2Registration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new MyFilter2()); registration.setName("myFilter2"); registration.addUrlPatterns("/*"); //此處盡量小,要比其他Filter靠前 registration.setOrder(2); return registration; } /** * 配置攔截器 * @return */ @Bean public FilterRegistrationBean exceptionFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new ExceptionFilter()); registration.setName("exceptionFilter"); //此處盡量小,要比其他Filter靠前 registration.setOrder(-1); return registration; }
方式三:BasicErrorController
其實spring boot原生提供了異常集中處理,我們經常會看到:
但是這種方式不夠友好,而且大部分情況不滿足我們的需求,我們可以通過繼承這個controller,然后重寫error方法或者errorHtml方法,或者兩個都重寫,區別是errorHtml是處理請求頭為text/html的請求發生的異常,而error是除了這個之外的其他異常。
下面是我定義的baseController,error部分返回的結果是空,還需要進一步的研究:
@RestController @RequestMapping(value = "error") public class MyBaseErrorController extends BasicErrorController { public MyBaseErrorController(ErrorAttributes errorAttributes) { super(errorAttributes, new ErrorProperties()); } @Override @RequestMapping(produces = {MediaType.ALL_VALUE}) public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { return new ResponseEntity<Map<String, Object>>(Result.failedResultMap(000, "未知錯誤"), HttpStatus.OK); } @RequestMapping( produces = {"text/html"} ) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = this.getStatus(request); response.setStatus(status.value()); return null; } }
最后,對于以上問題我最后的解決方法是繼承ErrorController,然后定義errorHtml和error,更重要的是@RequestMapping注解,然后在方法中response寫入返回值,這種方式不夠優雅:
@RestController @RequestMapping(value = "error") public class MyBaseErrorController implements ErrorController { private static final String path_default = "/error"; @Autowired private ErrorAttributes errorAttributes; @RequestMapping(produces = {MediaType.ALL_VALUE}) public void error(HttpServletRequest request, HttpServletResponse response) { setJsonError(response); } @RequestMapping( produces = {"text/html"} ) public void errorHtml(HttpServletRequest request, HttpServletResponse response) { setJsonError(response); } @Override public String getErrorPath() { return path_default; } private void setJsonError(HttpServletResponse response) { PrintWriter writer = null; try { response.setStatus(200); response.setHeader("Content-type", "text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); writer.write(JSON.toJSONString(Result.getFailed("未知錯誤", null))); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { writer.close(); } } } }
看完上述內容,是不是對Spring boot如何集中異常處理有進一步的了解,如果還想學習更多內容,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。