1. 程式人生 > >sprint-boot錯誤處理+錯誤處理的自動配置+定製錯誤響應

sprint-boot錯誤處理+錯誤處理的自動配置+定製錯誤響應

1瀏覽器訪問,返回錯誤給瀏覽器

2客戶端訪問,返回json給客戶端

 

1錯誤處理的自動配置

ErrorMvcAutoConfiguration

步驟:


1 一但系統出現4xx或者5xx之類的錯誤,ErrorPageCustomizer就會生效(定製錯誤的響應規則)

相當於發生錯誤後,返回/error請求,這裡會重定向
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {

   private final ServerProperties properties;

   private final DispatcherServletPath dispatcherServletPath;

   protected ErrorPageCustomizer(ServerProperties properties,
         DispatcherServletPath dispatcherServletPath) {
      this.properties = properties;
      this.dispatcherServletPath = dispatcherServletPath;
   }

   @Override
   public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
      ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath
            .getRelativePath(this.properties.getError().getPath()));//返回的是/error
      errorPageRegistry.addErrorPages(errorPage);
   }

   @Override
   public int getOrder() {
      return 0;
   }
}
  

 

2 就會發生/error請求

3  就會被BasicErrorController處理。

@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
   return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
         this.errorViewResolvers);
}
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {//處理/error請求
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)//處理瀏覽器的錯誤,返回html
public ModelAndView errorHtml(HttpServletRequest request,
      HttpServletResponse response) {
   HttpStatus status = getStatus(request);
   Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
         request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
   response.setStatus(status.value());
//去錯誤頁面設定
   ModelAndView modelAndView = resolveErrorView(request, response, status, model);
   return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}

@RequestMapping //處理客戶端錯誤,返回json
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
   Map<String, Object> body = getErrorAttributes(request,
         isIncludeStackTrace(request, MediaType.ALL));
   HttpStatus status = getStatus(request);
   return new ResponseEntity<>(body, status);
}

 4 DefaultErrorViewResolver決定去哪個頁面

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {

   private static final Map<Series, String> SERIES_VIEWS;

   static {

      Map<Series, String> views = new EnumMap<>(Series.class);
//處理4開頭的狀態碼
      views.put(Series.CLIENT_ERROR, "4xx");
      views.put(Series.SERVER_ERROR, "5xx");
      SERIES_VIEWS = Collections.unmodifiableMap(views);
   }
決定去哪個頁面
private ModelAndView resolve(String viewName, Map<String, Object> model) {
   String errorViewName = "error/" + viewName;
   TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
         .getProvider(errorViewName, this.applicationContext);
   if (provider != null) {
      return new ModelAndView(errorViewName, model);
   }
   return resolveResource(errorViewName, model);
}

private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
   for (String location : this.resourceProperties.getStaticLocations()) {
      try {
         Resource resource = this.applicationContext.getResource(location);
         resource = resource.createRelative(viewName + ".html");
         if (resource.exists()) {
            return new ModelAndView(new HtmlResourceView(resource), model);
         }
      }
      catch (Exception ex) {
      }
   }
   return null;
}

什麼程式碼決定有模板引擎情況下,會去/error下狀態碼對應的頁面。

如果沒有模板引擎就去找靜態資原始檔夾下狀態碼對應的頁面

如果沒有狀態碼對應的錯誤頁面,就去系統提供的預設頁面

 

2定製錯誤響應

 

1相應都是json資料

測試程式碼

@ControllerAdvice
public class MyExceptionHandler {
    //1、瀏覽器客戶端返回的都是json
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Map<String,Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        map.put("code","500");
        map.put("message","500錯誤");
        return map;
    }


}


@Controller
public class HelloController {


    @RequestMapping("/hello")
    public  void hello(){
        throw new RuntimeException("RuntimeException");
    }
}

2根據客戶端和瀏覽器來返回不同的方式

 

@ControllerAdvice
public class MyExceptionHandler {


    @ExceptionHandler(Exception.class)
    public String handleException(Exception e, HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();

        //設定錯誤狀態碼
        request.setAttribute("javax.servlet.error.status_code",520);
        map.put("code","520");
        map.put("message","520錯誤");

        request.setAttribute("ext",map);
        //轉發到/error,那裡會到瀏覽器和客戶端情況分別進行設定
        return "forward:/error";
    }
}

圖中一個是5xx頁面

 

 

 

3定製返回資料

在原有基礎上新增類

//給容器中加入我們自己定義的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    //返回值的map就是頁面和json能獲取的所有欄位
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {

         Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
        map.put("message1","message1");
        map.put("message2","message2");
        map.put("message3","message3");

        //我們的異常處理器攜帶的資料
        Map<String,Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
        map.put("ext",ext);
        return map;

    }

 
}