<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          springboot更優(yōu)雅的異常處理方式,了解下?

          共 13155字,需瀏覽 27分鐘

           ·

          2021-04-22 16:12

          集中異常處理

          以下代碼可在github獲取:

          https://github.com/Syske/learning-dome-code/tree/master/springboot-learning

          方式一:ExceptionHandle

          定義自己的異常類型,根據(jù)不同類型做不同處理,比如我定義的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以及攔截器的預(yù)處理方法中都可以完美捕獲,這里特殊說下攔截器:

          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");
                  /** 能捕獲異常信息并返回給客戶端,但并不會覆蓋已經(jīng)請求成功的返回結(jié)果,但會包含在返回結(jié)果中,比如我的返回結(jié)果:
                   {"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");
                  // 這里拋出的異常并不能被捕獲,會直接在后臺拋出,相當(dāng)于回掉函數(shù),請求結(jié)果已經(jīng)返回
              }
          }

          上面的解釋已經(jīng)夠清楚了,就不做過多說明了。剛剛我們說了,上面的這種方式,對于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,發(fā)生異常時直接轉(zhuǎn)發(fā)至前面定義的異常controller,這里需要注意的是,如果你的filter是實現(xiàn)Filter或者繼承OncePerRequestFilter,那你不需要任何處理,直接request.setAttribute("filter.error", e)就可以了。

          由于我把自己的filter交給shiro管理,而且是繼承BasicHttpAuthenticationFilter的,不知道什么原因,直接catch到的異常類型是ServletException,為了拿到真正的異常信息,我需要通過getCause()方法獲取filter中拋出的異常。因為controller拋出的異常最后還是會交給我們定義的MyExceptionHandle去處理,如果獲取到的異常不是我們自定義的異常或者他的子類的話,就會返回500錯誤(在這個示例前,我以為所有的filter都是這樣的,后來實踐后發(fā)現(xiàn)并不是這樣??)。

          @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);
                      //將異常分發(fā)到/error/exthrow控制器
                      request.getRequestDispatcher("/error/exthrow").forward(request, response);
                  }
              }

              @Override
              public void init(FilterConfig filterConfig) throws ServletException {
              }

              @Override
              public void destroy() {

              }
          }

          我的filter:

          實現(xiàn)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原生提供了異常集中處理,我們經(jīng)常會看到:

          但是這種方式不夠友好,而且大部分情況不滿足我們的需求,我們可以通過繼承這個controller,然后重寫error方法或者errorHtml方法,或者兩個都重寫,區(qū)別是errorHtml是處理請求頭為text/html的請求發(fā)生的異常,而error是除了這個之外的其他異常。

          下面是我定義的baseController,error部分返回的結(jié)果是空,還需要進一步的研究:

          @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寫入返回值,這種方式不夠優(yōu)雅??:

          @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();
                      }
                  }
              }

          }

          代碼可在github獲取:https://github.com/Syske/learning-dome-code/tree/master/springboot-learning

          - END -


          瀏覽 98
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  思瑞与土豪国产一区二区 | 一道本无码专区 | 欧美性精品 | 亚洲免费日本精品在线观看 | 天天好逼夜夜爽 |