一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

2021-06-15 11:07流煙默 Java教程

這篇文章主要介紹了SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

【1】springboot的默認錯誤處理

① 瀏覽器訪問

SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

請求頭如下:

SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

② 使用“postman”訪問

?
1
2
3
4
5
6
7
{
  "timestamp": 1529479254647,
  "status": 404,
  "error": "not found",
  "message": "no message available",
  "path": "/aaa1"
}

請求頭如下:

SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

總結:如果是瀏覽器訪問,則springboot默認返回錯誤頁面;如果是其他客戶端訪問,則默認返回json數據。

【2】默認錯誤處理原理

springboot默認配置了許多xxxautoconfiguration,這里我們找errormvcautoconfiguration。

其注冊部分組件如下:

① defaulterrorattributes

?
1
2
3
4
5
@bean
@conditionalonmissingbean(value = errorattributes.class, search = searchstrategy.current)
public defaulterrorattributes errorattributes() {
  return new defaulterrorattributes();
}

跟蹤其源碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
public class defaulterrorattributes
    implements errorattributes, handlerexceptionresolver, ordered {
 
  private static final string error_attribute = defaulterrorattributes.class.getname()
      + ".error";
 
  @override
  public int getorder() {
    return ordered.highest_precedence;
  }
 
  @override
  public modelandview resolveexception(httpservletrequest request,
      httpservletresponse response, object handler, exception ex) {
    storeerrorattributes(request, ex);
    return null;
  }
 
  private void storeerrorattributes(httpservletrequest request, exception ex) {
    request.setattribute(error_attribute, ex);
  }
 
  @override
  public map<string, object> geterrorattributes(requestattributes requestattributes,
      boolean includestacktrace) {
    map<string, object> errorattributes = new linkedhashmap<string, object>();
    errorattributes.put("timestamp", new date());
    addstatus(errorattributes, requestattributes);
    adderrordetails(errorattributes, requestattributes, includestacktrace);
    addpath(errorattributes, requestattributes);
    return errorattributes;
  }
 
  private void addstatus(map<string, object> errorattributes,
      requestattributes requestattributes) {
    integer status = getattribute(requestattributes,
        "javax.servlet.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(map<string, object> errorattributes,
      requestattributes requestattributes, boolean includestacktrace) {
    throwable error = geterror(requestattributes);
    if (error != null) {
      while (error instanceof servletexception && error.getcause() != null) {
        error = ((servletexception) error).getcause();
      }
      errorattributes.put("exception", error.getclass().getname());
      adderrormessage(errorattributes, error);
      if (includestacktrace) {
        addstacktrace(errorattributes, error);
      }
    }
    object message = getattribute(requestattributes, "javax.servlet.error.message");
    if ((!stringutils.isempty(message) || errorattributes.get("message") == null)
        && !(error instanceof bindingresult)) {
      errorattributes.put("message",
          stringutils.isempty(message) ? "no message available" : message);
    }
  }
 
  private void adderrormessage(map<string, object> errorattributes, throwable error) {
    bindingresult result = extractbindingresult(error);
    if (result == null) {
      errorattributes.put("message", error.getmessage());
      return;
    }
    if (result.geterrorcount() > 0) {
      errorattributes.put("errors", result.getallerrors());
      errorattributes.put("message",
          "validation failed for object='" + result.getobjectname()
              + "'. error count: " + result.geterrorcount());
    }
    else {
      errorattributes.put("message", "no errors");
    }
  }
 
  private bindingresult extractbindingresult(throwable error) {
    if (error instanceof bindingresult) {
      return (bindingresult) error;
    }
    if (error instanceof methodargumentnotvalidexception) {
      return ((methodargumentnotvalidexception) error).getbindingresult();
    }
    return null;
  }
 
  private void addstacktrace(map<string, object> errorattributes, throwable error) {
    stringwriter stacktrace = new stringwriter();
    error.printstacktrace(new printwriter(stacktrace));
    stacktrace.flush();
    errorattributes.put("trace", stacktrace.tostring());
  }
 
  private void addpath(map<string, object> errorattributes,
      requestattributes requestattributes) {
    string path = getattribute(requestattributes, "javax.servlet.error.request_uri");
    if (path != null) {
      errorattributes.put("path", path);
    }
  }
 
  @override
  public throwable geterror(requestattributes requestattributes) {
    throwable exception = getattribute(requestattributes, error_attribute);
    if (exception == null) {
      exception = getattribute(requestattributes, "javax.servlet.error.exception");
    }
    return exception;
  }
 
  @suppresswarnings("unchecked")
  private <t> t getattribute(requestattributes requestattributes, string name) {
    return (t) requestattributes.getattribute(name, requestattributes.scope_request);
  }
 
}

即,填充錯誤數據!

② basicerrorcontroller

?
1
2
3
4
5
6
@bean
@conditionalonmissingbean(value = errorcontroller.class, search = searchstrategy.current)
public basicerrorcontroller basicerrorcontroller(errorattributes errorattributes) {
  return new basicerrorcontroller(errorattributes, this.serverproperties.geterror(),
      this.errorviewresolvers);
}

跟蹤其源碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@controller
@requestmapping("${server.error.path:${error.path:/error}}")
public class basicerrorcontroller extends abstracterrorcontroller {
  //產生html類型的數據;瀏覽器發送的請求來到這個方法處理
  @requestmapping(produces = "text/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 ? new modelandview("error", model) : modelandview);
  }
  //產生json數據,其他客戶端來到這個方法處理;
  @requestmapping
  @responsebody
  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<map<string, object>>(body, status);
  }
  //...
}

其中 resolveerrorview(request, response, status, model);方法跟蹤如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class abstracterrorcontroller implements errorcontroller {
protected modelandview resolveerrorview(httpservletrequest request,
      httpservletresponse response, httpstatus status, map<string, object> model) {
      //拿到所有的錯誤視圖解析器
    for (errorviewresolver resolver : this.errorviewresolvers) {
      modelandview modelandview = resolver.resolveerrorview(request, status, model);
      if (modelandview != null) {
        return modelandview;
      }
    }
    return null;
  }
//...
}

③ errorpagecustomizer

?
1
2
3
4
@bean
public errorpagecustomizer errorpagecustomizer() {
  return new errorpagecustomizer(this.serverproperties);
}

跟蹤其源碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
@override
public void registererrorpages(errorpageregistry errorpageregistry) {
  errorpage errorpage = new errorpage(this.properties.getservletprefix()
      + this.properties.geterror().getpath());
  errorpageregistry.adderrorpages(errorpage);
}
//getpath()->go on
  /**
   * path of the error controller.
   */
  @value("${error.path:/error}")
  private string path = "/error";

即,系統出現錯誤以后來到error請求進行處理(web.xml注冊的錯誤頁面規則)。

④ defaulterrorviewresolver

?
1
2
3
4
5
6
7
@bean
@conditionalonbean(dispatcherservlet.class)
@conditionalonmissingbean
public defaulterrorviewresolver conventionerrorviewresolver() {
  return new defaulterrorviewresolver(this.applicationcontext,
      this.resourceproperties);
}

跟蹤其源碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class defaulterrorviewresolver implements errorviewresolver, ordered {
 
  private static final map<series, string> series_views;
  //錯誤狀態碼
  static {
    map<series, string> views = new hashmap<series, string>();
    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,
      map<string, object> model) {
  // 這里如果沒有拿到精確狀態碼(如404)的視圖,則嘗試拿4xx(或5xx)的視圖
    modelandview modelandview = resolve(string.valueof(status), model);
    if (modelandview == null && series_views.containskey(status.series())) {
      modelandview = resolve(series_views.get(status.series()), model);
    }
    return modelandview;
  }
 
  private modelandview resolve(string viewname, map<string, object> model) {
    //默認springboot可以去找到一個頁面? error/404||error/4xx
    string errorviewname = "error/" + viewname;
    //模板引擎可以解析這個頁面地址就用模板引擎解析
    templateavailabilityprovider provider = this.templateavailabilityproviders
        .getprovider(errorviewname, this.applicationcontext);
    if (provider != null) {
      //模板引擎可用的情況下返回到errorviewname指定的視圖地址
      return new modelandview(errorviewname, model);
    }
    //模板引擎不可用,就在靜態資源文件夾下找errorviewname對應的頁面 error/404.html
    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;
  }

總結如下:

一但系統出現4xx或者5xx之類的錯誤,errorpagecustomizer就會生效(定制錯誤的響應規則),就會來到/error請求,然后被basicerrorcontroller處理返回modelandview或者json。

【3】定制錯誤響應

① 定制錯誤響應頁面

1)有模板引擎的情況下

error/狀態碼–將錯誤頁面命名為 錯誤狀態碼.html 放在模板引擎文件夾里面的error文件夾下,發生此狀態碼的錯誤就會來到 對應的頁面。

我們可以使用4xx和5xx作為錯誤頁面的文件名來匹配這種類型的所有錯誤,精確優先(優先尋找精確的狀態碼.html)。

如下圖所示:

SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

頁面能獲取的信息;

timestamp:時間戳
status:狀態碼
error:錯誤提示
exception:異常對象
message:異常消息
errors:jsr303數據校驗的錯誤都在這里

2)沒有模板引擎(模板引擎找不到這個錯誤頁面),靜態資源文件夾下找。

3)以上都沒有錯誤頁面,就是默認來到springboot默認的錯誤提示頁面。

webmvcautoconfiguration源碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@configuration
@conditionalonproperty(prefix = "server.error.whitelabel", name = "enabled", matchifmissing = true)
@conditional(errortemplatemissingcondition.class)
protected static class whitelabelerrorviewconfiguration {
 
  private final spelview defaulterrorview = new spelview(
      "<html><body><h1>whitelabel error page</h1>"
          + "<p>this application has no explicit mapping for /error, so you are seeing this as a fallback.</p>"
          + "<div id='created'>${timestamp}</div>"
          + "<div>there was an unexpected error (type=${error}, status=${status}).</div>"
          + "<div>${message}</div></body></html>");
 
  @bean(name = "error")
  @conditionalonmissingbean(name = "error")
  public view defaulterrorview() {
    return this.defaulterrorview;
  }
 
  // if the user adds @enablewebmvc then the bean name view resolver from
  // webmvcautoconfiguration disappears, so add it back in to avoid disappointment.
  @bean
  @conditionalonmissingbean(beannameviewresolver.class)
  public beannameviewresolver beannameviewresolver() {
    beannameviewresolver resolver = new beannameviewresolver();
    resolver.setorder(ordered.lowest_precedence - 10);
    return resolver;
  }
 
}

② 定制錯誤響應數據

第一種,使用springmvc的異常處理器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@controlleradvice
public class myexceptionhandler {
 
  //瀏覽器客戶端返回的都是json
  @responsebody
  @exceptionhandler(usernotexistexception.class)
  public map<string,object> handleexception(exception e){
    map<string,object> map = new hashmap<>();
    map.put("code","user.notexist");
    map.put("message",e.getmessage());
    return map;
  }
}

這樣無論瀏覽器還是postman返回的都是json!

第二種,轉發到/error請求進行自適應效果處理

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@exceptionhandler(usernotexistexception.class)
public string handleexception(exception e, httpservletrequest request){
   map<string,object> map = new hashmap<>();
   //傳入我們自己的錯誤狀態碼 4xx 5xx
   /**
   * integer statuscode = (integer) request
   .getattribute("javax.servlet.error.status_code");
   */
   request.setattribute("javax.servlet.error.status_code",500);
   map.put("code","user.notexist");
   map.put("message","用戶出錯啦");
   //轉發到/error
   return "forward:/error";
 }

但是此時沒有將自定義 code message傳過去!

第三種,注冊myerrorattributes繼承自defaulterrorattributes(推薦)

從第【2】部分(默認錯誤處理原理)中知道錯誤數據都是通過defaulterrorattributes.geterrorattributes()方法獲取,如下所示:

?
1
2
3
4
5
6
7
8
9
10
@override
public map<string, object> geterrorattributes(requestattributes requestattributes,
    boolean includestacktrace) {
  map<string, object> errorattributes = new linkedhashmap<string, object>();
  errorattributes.put("timestamp", new date());
  addstatus(errorattributes, requestattributes);
  adderrordetails(errorattributes, requestattributes, includestacktrace);
  addpath(errorattributes, requestattributes);
  return errorattributes;
}

我們可以編寫一個myerrorattributes繼承自defaulterrorattributes重寫其geterrorattributes方法將我們的錯誤數據添加進去。

示例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//給容器中加入我們自己定義的errorattributes
@component
public class myerrorattributes extends defaulterrorattributes {
 
  //返回值的map就是頁面和json能獲取的所有字段
  @override
  public map<string, object> geterrorattributes(requestattributes requestattributes, boolean includestacktrace) {
    //defaulterrorattributes的錯誤數據
    map<string, object> map = super.geterrorattributes(requestattributes, includestacktrace);
    map.put("company","springboot");
    //我們的異常處理器攜帶的數據
    map<string,object> ext = (map<string, object>) requestattributes.getattribute("ext", 0);
    map.put("ext",ext);
    return map;
  }
}

異常處理器修改如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@exceptionhandler(usernotexistexception.class)
public string handleexception(exception e, httpservletrequest request){
   map<string,object> map = new hashmap<>();
   //傳入我們自己的錯誤狀態碼 4xx 5xx
   /**
   * integer statuscode = (integer) request
   .getattribute("javax.servlet.error.status_code");
   */
   request.setattribute("javax.servlet.error.status_code",500);
   map.put("code","user.notexist");
   map.put("message","用戶出錯啦");
  //將自定義錯誤數據放入request中
   request.setattribute("ext",map);
   //轉發到/error
   return "forward:/error";
 }

5xx.html頁面代碼如下:

?
1
2
3
4
5
6
7
8
9
10
//...
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
  <h1>status:[[${status}]]</h1>
  <h2>timestamp:[[${timestamp}]]</h2>
  <h2>exception:[[${exception}]]</h2>
  <h2>message:[[${message}]]</h2>
  <h2>ext:[[${ext.code}]]</h2>
  <h2>ext:[[${ext.message}]]</h2>
</main>
//...

瀏覽器測試效果如下:

SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

postman測試效果如下:

SpringBoot 錯誤處理機制與自定義錯誤處理實現詳解

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/j080624/article/details/80747669

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: oneday日本在线观看完整版 | 深夜免费在线观看 | 日韩一| 瘦老汉gay | 国产在线视频在线观看 | 四虎永久在线精品免费影视 | 欧美久在线观看在线观看 | 欧美老妪 | 国产欧美二区三区 | 国产51社区精品视频资源 | 关晓彤一级做a爰片性色毛片 | 国产精品一在线观看 | 精品亚洲视频在线观看 | 欧美亚洲一区二区三区在线 | 色久久一个亚洲综合网 | 校草让我脱了内裤给全班看 | 国产精品手机视频一区二区 | 天天天做天天天天爱天天想 | 男同桌脱我奶罩吸我奶作文 | 护士的小嫩嫩好紧好舒服 | 国产精品久久久久久搜索 | 免费视频片在线观看 | 亚洲精品www久久久久久久软件 | 国产免费好大好硬视频 | 午夜伦伦电影理论片大片 | fc2成人免费共享视频 | 麻生希在线观看 | 800精品国产导航 | 九九热免费在线观看 | 逼里逼里香| 2015小明台湾永久区域免费 | 95视频免费看片 | 久久成人永久免费播放 | 久久国产精品永久免费网站 | 国产性片在线观看 | 欧美艳星kagney1ynn | 亚洲AVAV天堂AV在线网爱情 | 天天快乐高清在线观看 | 我将她侵犯1~6樱花动漫在线看 | 国产在亚洲线视频观看 | 91香蕉视频导航 |