SpringBoot錯誤處理機制以及自定義異常響應原理解析

guoduan發表於2020-05-28

  SpringBoot錯誤處理機制以及自定義異常響應原理解析

  山就在那裡 2020-05-27 16:29:12 108 收藏 1

  分類專欄: Springboot學習

  版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

  本文連結:https://blog.csdn.net/xioayu96/article/details/106383627

  收起

  一、SpringBoot預設的錯誤處理機制

  首先看一下SpringBoot的預設處理結果

  這是頁面響應

  這是非頁面響應

  看到不同的響應資訊,大家可能有疑惑“訪問同一個地址,為什麼展示效果卻不一樣呢?”,其實這跟SpringBoot的處理機制有關係,下面就簡單講解一下它的錯誤處理機制。

  首先,SpringBoot所有的錯誤處理配置類ErrorMvcAutoConfiguration

  @Configuration(proxyBeanMethods = false)

  @ConditionalOnWebApplication(type = Type.SERVLET)

  @ConditionalOnClass({ Servlet.class, DispatcherServlet.class })

  // Load before the main WebMvcAutoConfiguration so that the error View is available

  @AutoConfigureBefore(WebMvcAutoConfiguration.class)

  @EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })

  public class ErrorMvcAutoConfiguration {DefaultErrorViewResolver

  其中裡面包含幾個重要的元件

  1、ErrorPageCustomizer:註冊錯誤頁面

  這個元件在發生請求之前就已經注入到Spring容器中了

  首先進入的就是這個元件,透過呼叫registerErrorPages方法的**getPath()**方法最終得到錯誤頁面路徑,

  errorPageRegistry.addErrorPages(errorPage);把/error路徑下對應的錯誤頁面注入到Servlet中,在請求訪問異常的時候,會轉發到 /error 路徑下

  @Bean

  public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {

  return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);

  }

  /**

  ErrorPageRegistry這個介面定義了在發生異常時該有哪些錯誤頁面被新增

  */

  @FunctionalInterface

  public interface ErrorPageRegistry {

  /**

  * Adds error pages that will be used when handling exceptions.

  * 新增處理異常時將使用的錯誤頁

  * @param errorPages the error pages

  */

  void addErrorPages(ErrorPage... errorPages);

  }

  @Override

  public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {

  ErrorPage errorPage = new ErrorPage(

  this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));

  //新增處理異常時將使用的錯誤頁

  errorPageRegistry.addErrorPages(errorPage);

  }

  public String getPath() {

  return this.path;

  }

  @Value("${error.path:/error}")

  private String path = "/error";

  2、BasicErrorController:SpringBoot統一異常處理Controller

  errorHtml:用來響應瀏覽器請求時的錯誤資訊

  MediaType.TEXT_HTML_VALUE:就代表了瀏覽器發起的請求,因為TEXT_HTML_VALUE = "text/html"

  error用來響應非瀏覽器請求時的錯誤資訊,返回的是json格式

  這也就解釋了文章開頭為什麼用不同的方式請求會有不同的響應資訊展示

  /**

  * A String equivalent of {@link MediaType#TEXT_HTML}.

  */

  public static final String TEXT_HTML_VALUE = "text/html";

  //這個方法時用來響應瀏覽器請求時的錯誤資訊

  @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)

  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {

  HttpStatus status = getStatus(request);

  Mapmodel = Collections

  .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));

  response.setStatus(status.value());

  ModelAndView modelAndView = resolveErrorView(request, response, status, model);

  return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);

  }

  //這個方法時用來響應非瀏覽器請求時的錯誤資訊

  @RequestMapping

  public ResponseEntity<1map> error(HttpServletRequest request) {

  HttpStatus status = getStatus(request);

  if (status == HttpStatus.NO_CONTENT) {

  return new ResponseEntity<>(status);

  }

  Mapbody = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));

  return new ResponseEntity<>(body, status);

  }

  3、DefaultErrorViewResolver:解析要跳轉到哪個錯誤頁面

  原始碼如下

  public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {

  static {

  Mapviews = new EnumMap<>(Series.class);

  views.put(Series.CLIENT_ERROR, "4xx");

  views.put(Series.SERVER_ERROR, "5xx");

  SERIES_VIEWS = Collections.unmodifiableMap(views);

  }

  @Override

  public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Mapmodel) {

  ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);

  if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {

  modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);

  }

  return modelAndView;

  }

  private ModelAndView resolve(String viewName, Mapmodel) {

  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, Mapmodel) {

  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;

  }

  }

  透過上面BasicErrorController判斷是否為瀏覽器請求,如果非瀏覽器請求,就會呼叫error方法返回json資訊;如果是瀏覽器請求,這時候就會進入到DefaultErrorViewResolver的**resolveErrorView()**方法

  分兩種情況:第一種可以透過模板引擎解析的頁面;第二種模板引擎無法解析的頁面

  以上兩種情況又各分為兩種情況,可以精確匹配到的頁面,比如404.html;另一種則是模糊匹配頁面,比如4xx.html、5xx.html.

  第一種:先說一下可以透過模板引擎解析的頁面的精確匹配,首先這個頁面是需要放在 /template/error/ 路徑下的,當請求進入後,模板引擎會根據狀態碼解析錯誤頁面,精確匹配到路徑下的頁面,功能實現就是這句this.templateAvailabilityProviders.getProvider(errorViewName,this.applicationContext);

  然後直接返回一個ModelAndView物件,如圖,有頁面則直接返回

  第二種:模板引擎無法解析的頁面和靜態資原始檔夾下也沒有頁面的情況,這時候this.templateAvailabilityProviders.getProvider(errorViewName,this.applicationContext);會返回一個null,從而進入到 resolveResource() 方法,透過遍歷靜態資原始檔位置獲取匹配的頁面,如果沒有頁面,則會返回系統預設的檢視物件,截圖展示

  開始呼叫**resolveResource()方法遍歷所有靜態資源路徑

  先找 META-INFO/resources/error/ 路徑下有沒有匹配的頁面

  再找當前專案路徑下/resources/error/下有沒有匹配的頁面

  然後找當前專案路徑下/static/error/下有沒有匹配的頁面

  最後找當前專案路徑下/public/error/**下有沒有匹配的頁面

  最後沒有找到對應的頁面,返回一個null,SpringBoot則會使用預設的頁面展示,也就是文章開頭那個圖

  如果對應的靜態資原始檔下有頁面則會直接匹配上,如圖

  透過分析,模板引擎只會解析/Template/error路徑下的頁面

  這裡還要說明一點內容: 原始碼定義了模糊匹配規則

  public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {

  static {

  Mapviews = new EnumMap<>(Series.class);

  views.put(Series.CLIENT_ERROR, "4xx");

  views.put(Series.SERVER_ERROR, "5xx");

  SERIES_VIEWS = Collections.unmodifiableMap(views);

  }

  }

  views.put(Series.CLIENT_ERROR, “4xx”);

  views.put(Series.SERVER_ERROR, “5xx”);

  如果在精確匹配的情況下找不到頁面,賊會使用模糊匹配的方式查詢,就像這種4xx,5xx,截圖在下面

  上面是分析的精確匹配頁面,接下來就看一下模糊匹配**

  這時候把我選擇把頁面放在了 /resources/error/ 資料夾下,接下來的 迴圈遍歷跟上面精確匹配邏輯一樣,就不一一展示了,這裡匹配到了;如果匹配不到返回一個null,SpringBoot則會使用預設的頁面展示,也就是文章開頭那個圖

  最終展示效果如圖

  透過上面遍歷尋找頁面可以得出一個結論:精確優先,尋找位置的優先順序(從高到底),從原始碼也可以看出

  META-INFO/resources/error/

  專案路徑下/resources/error/

  專案路徑下/static/error/

  專案路徑下/public/error/

  原始碼如下:

  @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)

  public class ResourceProperties {

  private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/" };

  /**

  * Locations of static resources. Defaults to classpath:[/META-INF/resources/,

  * /resources/, /static/, /public/].

  */

  private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;

  4、DefaultErrorAttributes:註冊了一個專門收集 error 發生時錯誤資訊的bean,把錯誤資訊注入到錯誤頁面中

  類DefaultErrorAttributes中的 getErrorAttributes() 方法提供了預設的錯誤資訊注入,可以獲取到

  getErrorAttributes()方法獲取timestamp:時間戳

  addStatus()方法獲取status:狀態碼

  addErrorDetails()方法獲取exception:異常物件

  addStackTrace()方法獲取trace:堆疊資訊(能力尚淺,僅是猜測)

  addExceptionErrorMessage()方法獲取message:異常錯誤資訊

  addBindingResultErrorMessage()方法獲取errors:JSR303校驗資訊

  addPath()獲取請求出錯的路徑path:錯誤路徑

  public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {

  @Override

  public MapgetErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {

  MaperrorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));

  return errorAttributes;

  }

  //時間戳

  public MapgetErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {

  MaperrorAttributes = new LinkedHashMap<>();

  //獲取時間戳

  errorAttributes.put("timestamp", new Date());

  addStatus(errorAttributes, webRequest);

  addErrorDetails(errorAttributes, webRequest, includeStackTrace);

  addPath(errorAttributes, webRequest);

  return errorAttributes;

  }

  //狀態碼

  private void addStatus(MaperrorAttributes, RequestAttributes requestAttributes) {

  Integer status = getAttribute(requestAttributes, RequestDispatcher.ERROR_STATUS_CODE);

  if (status == null) {

  errorAttributes.put("status", 999);

  errorAttributes.put("error", "None");

  return;

  }

  //獲取狀態碼

  errorAttributes.put("status", status);

  try {

  errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());

  }

  catch (Exception ex) {

  // Unable to obtain a reason

  errorAttributes.put("error", "Http Status " + status);

  }

  }

  //異常物件

  private void addErrorDetails(MaperrorAttributes, WebRequest webRequest,

  boolean includeStackTrace) {

  Throwable error = getError(webRequest);

  if (error != null) {

  while (error instanceof ServletException && error.getCause() != null) {

  error = error.getCause();

  }

  //獲取異常物件

  errorAttributes.put("exception", error.getClass().getName());

  if (includeStackTrace) {

  addStackTrace(errorAttributes, error);

  }

  }

  addErrorMessage(errorAttributes, webRequest, error);

  }

  //異常資訊和JSR303校驗資訊的入口

  private void addErrorMessage(MaperrorAttributes, WebRequest webRequest, Throwable error) {

  BindingResult result = extractBindingResult(error);

  if (result == null) {

  addExceptionErrorMessage(errorAttributes, webRequest, error);

  }

  else {

  addBindingResultErrorMessage(errorAttributes, result);

  }

  }

  //異常錯誤資訊

  private void addExceptionErrorMessage(MaperrorAttributes, WebRequest webRequest, Throwable error) {

  Object message = getAttribute(webRequest, RequestDispatcher.ERROR_MESSAGE);

  if (StringUtils.isEmpty(message) && error != null) {

  message = error.getMessage();

  }

  if (StringUtils.isEmpty(message)) {

  message = "No message available";

  }

  errorAttributes.put("message", message);

  }

  //JSR303資料校驗錯誤資訊

  private void addBindingResultErrorMessage(MaperrorAttributes, BindingResult result) {

  errorAttributes.put("message", "Validation failed for object='" + result.getObjectName() + "'. "

  + "Error count: " + result.getErrorCount());

  errorAttributes.put("errors", result.getAllErrors());

  }

  //目前能力尚淺,猜測是獲取堆資訊

  private void addStackTrace(MaperrorAttributes, Throwable error) {

  StringWriter stackTrace = new StringWriter();

  error.printStackTrace(new PrintWriter(stackTrace));

  stackTrace.flush();

  errorAttributes.put("trace", stackTrace.toString());

  }

  //請求路徑

  private void addPath(MaperrorAttributes, RequestAttributes requestAttributes) {

  String path = getAttribute(requestAttributes, RequestDispatcher.ERROR_REQUEST_URI);

  if (path != null) {

  errorAttributes.put("path", path);

  }

  }

  }

  二、自定義錯誤響應

  1、定製錯誤頁面

  1)、有模板引擎的情況下;

  error/狀態碼,【將錯誤頁面命名為 錯誤狀態碼.html 放在模板引擎資料夾裡面的 error資料夾下】,發生此狀態碼的錯誤就會來到對應的頁面;我們可以使用4xx和5xx作為錯誤頁面的檔名來匹配這種型別的所有錯誤,精確優先(優先尋找精確的狀態碼.html)

  獲取定製頁面的錯誤資訊,在錯誤頁面直接獲取上面DefaultErrorAttributes提到的那些資訊就可以

  2)、沒有模板引擎(模板引擎找不到這個錯誤頁面)

  靜態資原始檔夾下找,第一部分錯誤處理機制中的第3節有提到

  3)、以上都沒有錯誤頁面

  就是預設來到SpringBoot預設的錯誤提示頁面

  預設資訊注入以及預設頁面響應

  2、定製錯誤的json資料

  先看一個預設的錯誤的json資料

  這裡面只包含了

  timestamp:時間戳

  status:狀態碼

  error:錯誤提示

  message:異常訊息

  path:路徑

  由上圖可以看到error和message資訊並沒有展示出來,這是SpringBoot1.0和2.0的一點區別,2.0預設是關閉狀態,需要在主配置檔案配置一下

  server.error.include-exception=true

  server.error.include-message=always

  1)、自定義異常處理&返回定製json資料

  首先編寫幾個測試需要的類

  /**

  * @PackageName : cn.rain.springboot.excepiton

  * @Created : 2020/5/27

  * @Author : Rain

  * @Version : V1.0

  * @Des : 自定義異常用來測試定製錯誤的json資料

  */

  public class MyException extends RuntimeException {

  public MyException() {

  super("使用者名稱錯誤!");

  }

  }

  /**

  * @PackageName : cn.rain.springboot.controller

  * @Created : 2020/5/27

  * @Author : Rain

  * @Version : V1.0

  * @Des : 自定義異常處理器,這裡還達不到自適應效果,瀏覽器和其他客戶端展示都是json格式資料

  */

  @ControllerAdvice

  public class MyexceptionHandler {

  @ResponseBody

  @ExceptionHandler(MyException.class)

  public MaphanderException(Exception e, HttpServletRequest request){

  Mapmap = new HashMap<>();

  map.put("msg1","我在學習SpringBoot自定義異常");

  map.put("msg2","這是自定義異常資訊:"+e.getMessage());

  map.put("company","阿里爸爸");

  return map;

  }

  }

  這裡就已經達到了自定義json格式資料,不再使用SpringBoot提供的格式,如圖展示了頁面和其他客戶端資料樣式

  其實這樣很不友好,無論從什麼客戶端發起請求,都會返回json格式資料,那怎麼才可以達到自適應效果呢,往下看

  2)、轉發到/error進行自適應響應效果處理

  /**

  * @PackageName : cn.rain.springboot.controller

  * @Created : 2020/5/27

  * @Author : Rain

  * @Version : V1.0

  * @Des : 自定義異常處理器,這裡在第1步的基礎上做了修改,新增了狀態碼,改變了返回值

  */

  @ControllerAdvice

  public class MyexceptionHandler {

  @ExceptionHandler(MyException.class)

  public String handerException(Exception e, HttpServletRequest request){

  Mapmap = new HashMap<>();

  //自定義資訊

  map.put("msg1","我在學習SpringBoot自定義異常");

  map.put("msg2","這是自定義異常資訊:"+e.getMessage());

  map.put("company","阿里爸爸");

  request.setAttribute("javax.servlet.error.status_code",400);

  request.setAttribute("ext",map);

  //轉發到/error路徑下,這裡其實最終還是交給了SpringBoot來處理

  return "forward:/error";

  }

  }

  經過在第1步的基礎上做了修改,新增了狀態碼,改變了返回值,這時候已經看出效果了,但是我們自己新增的資訊好像沒有展示出來

  //自定義資訊

  map.put(“code”,e.getClass());

  map.put(“msg”,e.getMessage());

  首先說一下這一步實現自適應效果,原理還是使用的SpringBoot底層的BasicErrorController這個元件,因為它是自適應的

  @RequestMapping("KaTeX parse error: Expected '}', got 'EOF' at end of input: …ver.error.path:{error.path:/error}}")

  因為我們是跳轉到了 /error路徑下,所以 BasicErrorController會去 /error路徑下尋找頁面;如果是其他客戶端請求,就會返回json資料,這樣就達到了自適應效果,BasicErrorController這個元件的具體講解請參考SpringBoot錯誤處理機制的1.2節

  @Controller

  @RequestMapping("${server.error.path:${error.path:/error}}")

  public class BasicErrorController extends AbstractErrorController {

  //下面這個兩個方法就是SpringBoot為我們提供的

  @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)

  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {

  HttpStatus status = getStatus(request);

  }

  @RequestMapping

  public ResponseEntity<1map> error(HttpServletRequest request) {

  HttpStatus status = getStatus(request);

  }

  }

  可以看到這兩個方法中都有一段共同的程式碼HttpStatus status = getStatus(request);,它的作用就是為了給解析檢視提供狀態碼,透過下面的原始碼request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)可以看出來,SpringBoot首先是透過request域物件中獲取狀態碼,但是這裡有一個引數RequestDispatcher.ERROR_STATUS_CODE,這個引數的具體值就是javax.servlet.error.status_code

  所以我們在自定義異常處理器時,需要手動指定狀態碼,否則不生效

  request.setAttribute(“javax.servlet.error.status_code”,400);

  protected HttpStatus getStatus(HttpServletRequest request) {

  Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);

  if (statusCode == null) {

  return HttpStatus.INTERNAL_SERVER_ERROR;

  }

  try {

  return HttpStatus.valueOf(statusCode);

  }

  catch (Exception ex) {

  return HttpStatus.INTERNAL_SERVER_ERROR;

  }

  }

  public interface RequestDispatcher {

  public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";

  }

  到這裡,我們已經可以做到了自定義異常處理器的自適應效果,但是還有一個問題,就是無論頁面還是其他客戶端展示的資訊還是系統預設的,我們自定義的異常資訊沒有展示,接下來繼續改善它

  3)、將我們的定製資料攜帶到頁面或者其他客戶端

  /**

  * @PackageName : cn.rain.springboot.component

  * @Created : 2020/5/27

  * @Author : Rain

  * @Version : V1.0

  * @Des : 自定義的ErrorAttributes,用來處理攜帶的自定義資訊

  */

  @Configuration

  public class MyErrorAttributes extends DefaultErrorAttributes {

  /**

  * 重寫getErrorAttributes方法,這個方法也就是在SpringBoot預設的錯誤處理機制的1.4講過的那個方法

  * @param webRequest

  * @param options

  * @return

  */

  @Override

  public MapgetErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {

  //呼叫父類的getErrorAttributes()方法,返回Map物件

  MaperrorAttributes = super.getErrorAttributes(webRequest, options);

  //新增自定義異常資訊,首先獲取到request域物件中自己新增的資訊,這是我們在自定義異常處理器手動新增進去的異常資訊

  //自定義資訊

  //map.put("msg1","我在學習SpringBoot自定義異常");

  //map.put("msg2","這是自定義異常資訊:"+e.getMessage());

  //map.put("company","阿里爸爸");

  Mapext = (Map) webRequest.getAttribute("ext", 0);

  errorAttributes.put("ext",ext);

  return errorAttributes;

  }

  }

  效果圖如下

  下面我們來說一下原理,從上面MyErrorAttributes這個類可以看出,它繼承了DefaultErrorAttributes,是不是感覺很眼熟,它就是出現在SpringBoot錯誤處理機制的1.4節,不懂的小夥伴,可以往上翻一番。先看一段原始碼

  public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {

  @Override

  public MapgetErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {

  MaperrorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));

  if (this.includeException != null) {

  options = options.including(Include.EXCEPTION);

  }

  if (!options.isIncluded(Include.EXCEPTION)) {

  errorAttributes.remove("exception");

  }

  if (!options.isIncluded(Include.STACK_TRACE)) {

  errorAttributes.remove("trace");

  }

  if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {

  errorAttributes.put("message", "");

  }

  if (!options.isIncluded(Include.BINDING_ERRORS)) {

  errorAttributes.remove("errors");

  }

  return errorAttributes;

  }

  }

  看完原始碼後,小夥伴是不是又感覺好眼熟,對的,你的感覺沒有錯,SpringBoot錯誤處理機制的1.4節具體講過,這裡我們需要看的是這段程式碼

  public MapgetErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {

  MaperrorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));

  1

  2

  這裡的 getErrorAttributes() 方法就是我們重寫的那個方法,這個方法裡面包含了這段程式碼

  MaperrorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));

  1

  為我們返回了系統預設格式的異常資訊集合,我們就是在這個基礎上擴充套件自己定義的異常資訊,所以就出現了這一小節開頭的那段程式碼

  /**

  * @PackageName : cn.rain.springboot.component

  * @Created : 2020/5/27

  * @Author : Rain

  * @Version : V1.0

  * @Des : 自定義的ErrorAttributes,用來處理攜帶的自定義資訊

  */

  @Configuration

  public class MyErrorAttributes extends DefaultErrorAttributes {

  /**

  * 重寫getErrorAttributes方法,這個方法也就是在1.1.4講過的那個方法

  * @param webRequest

  * @param options

  * @return

  */

  @Override

  public MapgetErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {

  //呼叫父類的getErrorAttributes()方法,返回Map物件

  MaperrorAttributes = super.getErrorAttributes(webRequest, options);

  //新增自定義異常資訊,首先獲取到request域物件中自己新增的資訊

  Mapext = (Map) webRequest.getAttribute("ext", 0);

  errorAttributes.put("ext",ext);

  return errorAttributes;

  }

  }

  MaperrorAttributes = super.getErrorAttributes(webRequest, options);

  作用是呼叫父類的 getErrorAttributes() 方法,返回原生的異常資訊集合

  Mapext = (Map) webRequest.getAttribute(“ext”, 0);

  新增自定義異常資訊,首先獲取到request域物件中自己新增的資訊(request域物件資訊是我們在2.2.2小節新增的request.setAttribute(“ext”,map);)

  這裡我們再說一下 webRequest.getAttribute(“ext”, 0);,第一個引數就不用解釋了,獲取域物件中封裝的資訊;那這個0的是用來幹嘛的呢?看一下原始碼

  public interface RequestAttributes {

  /**

  * Constant that indicates request scope.

  */

  int SCOPE_REQUEST = 0;

  @Nullable

  Object getAttribute(String name, int scope);

  }

  我們自己寫的這個類MyErrorAttributes繼承自DefaultErrorAttributes,而DefaultErrorAttributes又實現了了RequestAttributes介面,所以,我們必須要給定第二個引數,這就出現了0,到這裡我們應該就可以理解了,這個0就是用來確定request域物件的,這樣我們就可以從request域物件中獲取之前自定義的異常資訊了。

  到這裡關於SpringBoot的錯誤處理機制以及自定義錯誤響應頁面和自定義異常資訊就說的差不多了,本人剛開始學,實在是能力有限,以上只是個人理解,如有寫的不對的地方,敬請批評指正。

  ————————————————

  版權宣告:本文為CSDN博主「山就在那裡」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。

   原文連結: https://blog.csdn.net/xioayu96/article/details/106383627


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69971700/viewspace-2694820/,如需轉載,請註明出處,否則將追究法律責任。

相關文章