激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

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

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

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 亚洲第一精品在线 | 黄色片视频免费观看 | 天堂成人一区二区三区 | 久久久电影电视剧免费看 | 综合精品| 国产日韩中文字幕 | 在线成人www免费观看视频 | 羞羞的视频免费在线观看 | 免费淫视频 | 爱逼爱操综合网 | 欧美在线黄色 | 一级片久久免费 | 在线免费观看日韩视频 | 成码无人av片在线观看网站 | 国产免费v片 | 久久男人视频 | 狠狠干91| 日本免费成人网 | 色女人在线 | 久久精品视频黄色 | 青青草成人影视 | 亚洲第五色综合网 | 日本免费成人网 | 一区二区视 | 亚洲免费资源 | 国产精品久久久久久久久久久久午夜 | 久久久久久69 | 看毛片的网址 | 视频一区二区三区在线观看 | 蜜桃成品人免费视频 | 亚洲欧美日韩在线 | 永久av在线免费观看 | 欧美成视频在线观看 | 黄色的视频在线观看 | 久青草免费视频 | 一本色道久久99精品综合蜜臀 | 男人天堂新地址 | 成人免费毛片网站 | 亚洲国产网址 | 亚洲午夜精选 | 91一区二区三区久久久久国产乱 |