一、前言
由于客戶端的環境不一致,有可能會造成我們預計不到的異常錯誤,所以在項目中,友好的異常信息提示,是非常重要的。在asp.net mvc中實現異常屬性攔截也非常簡單,只需要繼承另一個類(System.Web.Mvc.FilterAttribute)和一個接口(System.Web.Mvc.IExceptionFilter),實現接口里面OnException方法,或者直接繼承Mvc 提供的類System.Web.Mvc.HandleErrorAttribute。
下面話不多說了,來一起看看詳細的介紹吧
二、實現關鍵邏輯
繼承System.Web.Mvc.HandleErrorAttribute,重寫了OnException方法,主要實現邏輯代碼如下:
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
|
public class HandlerErrorAttribute : HandleErrorAttribute { /// <summary> /// 控制器方法中出現異常,會調用該方法捕獲異常 /// </summary> /// <param name="context">提供使用</param> public override void OnException(ExceptionContext context) { WriteLog(context); base .OnException(context); context.ExceptionHandled = true ; if (context.Exception is UserFriendlyException) { context.HttpContext.Response.StatusCode = ( int )HttpStatusCode.OK; context.Result = new ContentResult { Content = new AjaxResult { type = ResultType.error, message = context.Exception.Message }.ToJson() }; } else if (context.Exception is NoAuthorizeException) { context.HttpContext.Response.StatusCode = ( int )HttpStatusCode.Unauthorized; if (!context.HttpContext.Request.IsAjaxRequest()) { context.HttpContext.Response.RedirectToRoute( "Default" , new { controller = "Error" , action = "Error401" , errorUrl = context.HttpContext.Request.RawUrl }); } else { context.Result = new ContentResult { Content = context.HttpContext.Request.RawUrl }; } } else { context.HttpContext.Response.StatusCode = ( int )HttpStatusCode.InternalServerError; ExceptionMessage error = new ExceptionMessage(context.Exception); var s = error.ToJson(); if (!context.HttpContext.Request.IsAjaxRequest()) { context.HttpContext.Response.RedirectToRoute( "Default" , new { controller = "Error" , action = "Error500" , data = WebHelper.UrlEncode(s) }); } else { context.Result = new ContentResult { Content = WebHelper.UrlEncode(s) }; } } } /// <summary> /// 寫入日志(log4net) /// </summary> /// <param name="context">提供使用</param> private void WriteLog(ExceptionContext context) { if (context == null ) return ; if (context.Exception is NoAuthorizeException || context.Exception is UserFriendlyException) { //友好錯誤提示,未授權錯誤提示,記錄警告日志 LogHelper.Warn(context.Exception.Message); } else { //異常錯誤, LogHelper.Error(context.Exception); ////TODO :寫入錯誤日志到數據庫 } } } |
MVC 過濾器全局注冊異常攔截:
1
2
3
4
5
6
7
|
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add( new HandlerErrorAttribute()); } } |
我們看到,context.Exception 分為3種:UserFriendlyException,NoAuthorizeException 或 Exception;UserFriendlyException 是指友好異常,前端友好提示錯誤信息。NoAuthorizeException 為401未授權異常,當頁面未被授權訪問時,返回該異常,并攜帶有未授權的路徑地址。其他異常統一返回500錯誤,并攜帶異常信息。
三、異常處理
1.401 未授權錯誤
異常定義代碼:
1
2
3
4
5
6
7
8
9
10
|
/// <summary> /// 沒有被授權的異常 /// </summary> public class NoAuthorizeException : Exception { public NoAuthorizeException( string message) : base (message) { } } |
拋出異常代碼:
1
|
throw new NoAuthorizeException( "未授權" ); |
前端UI效果:
2.404 未找到頁面錯誤
MVC的404異常處理,有幾種方式,我們采用了在Global.asax全局請求函數中處理, 請查看以下代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
protected void Application_EndRequest() { if (Context.Response.StatusCode == 404) { bool isAjax = new HttpRequestWrapper(Context.Request).IsAjaxRequest(); if (isAjax) { Response.Clear(); Response.Write(Context.Request.RawUrl); } else { Response.RedirectToRoute( "Default" , new { controller = "Error" , action = "Error404" , errorUrl = Context.Request.RawUrl }); } } } |
前端UI效果:
3.500服務器內部錯誤
500異常錯誤拋出的異常信息對象定義:
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
|
/// <summary> /// 異常錯誤信息 /// </summary> [Serializable] public class ExceptionMessage { public ExceptionMessage() { } /// <summary> /// 構造函數 /// 默認顯示異常頁面 /// </summary> /// <param name="ex">異常對象</param> public ExceptionMessage(Exception ex) : this (ex, true ) { } /// <summary> /// 構造函數 /// </summary> /// <param name="ex">異常對象</param> /// <param name="isShowException">是否顯示異常頁面</param> public ExceptionMessage(Exception ex, bool isShowException) { MsgType = ex.GetType().Name; Message = ex.InnerException != null ? ex.InnerException.Message : ex.Message; StackTrace = ex.StackTrace.Length > 300 ? ex.StackTrace.Substring(0, 300) : ex.StackTrace; Source = ex.Source; Time = DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" ); Assembly = ex.TargetSite.Module.Assembly.FullName; Method = ex.TargetSite.Name; ShowException = isShowException; var request = HttpContext.Current.Request; IP = Net.Ip; UserAgent = request.UserAgent; Path = request.Path; HttpMethod = request.HttpMethod; } /// <summary> /// 消息類型 /// </summary> public string MsgType { get ; set ; } /// <summary> /// 消息內容 /// </summary> public string Message { get ; set ; } /// <summary> /// 請求路徑 /// </summary> public string Path { get ; set ; } /// <summary> /// 程序集名稱 /// </summary> public string Assembly { get ; set ; } /// <summary> /// 異常參數 /// </summary> public string ActionArguments { get ; set ; } /// <summary> /// 請求類型 /// </summary> public string HttpMethod { get ; set ; } /// <summary> /// 異常堆棧 /// </summary> public string StackTrace { get ; set ; } /// <summary> /// 異常源 /// </summary> public string Source { get ; set ; } /// <summary> /// 服務器IP 端口 /// </summary> public string IP { get ; set ; } /// <summary> /// 客戶端瀏覽器標識 /// </summary> public string UserAgent { get ; set ; } /// <summary> /// 是否顯示異常界面 /// </summary> public bool ShowException { get ; set ; } /// <summary> /// 異常發生時間 /// </summary> public string Time { get ; set ; } /// <summary> /// 異常發生方法 /// </summary> public string Method { get ; set ; } } |
拋出異常代碼:
1
|
throw new Exception( "出錯了" ); |
前端UI效果:
4. UserFriendlyException 友好異常
異常定義代碼:
1
2
3
4
5
6
7
8
9
10
|
/// <summary> /// 用戶友好異常 /// </summary> public class UserFriendlyException : Exception { public UserFriendlyException( string message) : base (message) { } } |
在異常攔截關鍵代碼中,我們發現友好異常(UserFriendlyException)其實是返回了一個結果對象AjaxResult,
AjaxResult對象的定義:
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
|
/// <summary> /// 表示Ajax操作結果 /// </summary> public class AjaxResult { /// <summary> /// 獲取 Ajax操作結果類型 /// </summary> public ResultType type { get ; set ; } /// <summary> /// 獲取 Ajax操作結果編碼 /// </summary> public int errorcode { get ; set ; } /// <summary> /// 獲取 消息內容 /// </summary> public string message { get ; set ; } /// <summary> /// 獲取 返回數據 /// </summary> public object resultdata { get ; set ; } } /// <summary> /// 表示 ajax 操作結果類型的枚舉 /// </summary> public enum ResultType { /// <summary> /// 消息結果類型 /// </summary> info = 0, /// <summary> /// 成功結果類型 /// </summary> success = 1, /// <summary> /// 警告結果類型 /// </summary> warning = 2, /// <summary> /// 異常結果類型 /// </summary> error = 3 } |
四、Ajax請求異常時處理
在異常攔截的關鍵代碼中,我們有看到,如果是ajax請求時,是執行不同的邏輯,這是因為ajax的請求,不能直接通過MVC的路由跳轉,在請求時必須返回結果內容
然后在前端ajax的方法中,統一處理返回的錯誤,以下是我們項目中用到的ajax封裝,對異常錯誤,進行了統一處理。
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
(function ($) { "use strict" ; $.httpCode = { success: "1" , fail: "3" , }; // http 通信異常的時候調用此方法 $.httpErrorLog = function (msg) { console.log( '=====>' + new Date().getTime() + '<=====' ); console.log(msg); }; // ajax請求錯誤處理 $.httpError = function (xhr, textStatus, errorThrown) { if (xhr.status == 401) { location.href = "/Error/Error401?errorUrl=" + xhr.responseText; } if (xhr.status == 404) { location.href = "/Error/Error404?errorUrl=" + xhr.responseText; } if (xhr.status == 500) { location.href = "/Error/Error500?data=" + xhr.responseText; } }; /* get請求方法(異步): * url地址, param參數, callback回調函數 beforeSend 請求之前回調函數, complete 請求完成之后回調函數 * 考慮到get請求一般將參數與url拼接一起傳遞,所以將param參數放置最后 * 返回AjaxResult結果對象 */ $.httpAsyncGet = function (url, callback, beforeSend, complete, param) { $.ajax({ url: url, data: param, type: "GET" , dataType: "json" , async: true , cache: false , success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, beforeSend: function () { if (!!beforeSend) beforeSend(); }, complete: function () { if (!!complete) complete(); } }); }; /* get請求方法(同步): * url地址,param參數 * 返回實體數據對象 */ $.httpGet = function (url, param) { var res = {}; $.ajax({ url: url, data: param, type: "GET" , dataType: "json" , async: false , cache: false , success: function (data) { res = data; }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, }); return res; }; /* post請求方法(異步): * url地址, param參數, callback回調函數 beforeSend 請求之前回調函數, complete 請求完成之后回調函數 * 返回AjaxResult結果對象 */ $.httpAsyncPost = function (url, param, callback, beforeSend, complete) { $.ajax({ url: url, data: param, type: "POST" , dataType: "json" , async: true , cache: false , success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, beforeSend: function () { if (!!beforeSend) beforeSend(); }, complete: function () { if (!!complete) complete(); } }); }; /* post請求方法(同步): * url地址,param參數, callback回調函數 * 返回實體數據對象 */ $.httpPost = function (url, param, callback) { $.ajax({ url: url, data: param, type: "POST" , dataType: "json" , async: false , cache: false , success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, }); }, /* ajax異步封裝: * type 請求類型, url地址, param參數, callback回調函數 * 返回實體數據對象 */ $.httpAsync = function (type, url, param, callback) { $.ajax({ url: url, data: param, type: type, dataType: "json" , async: true , cache: false , success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, }); }; })(jQuery); |
五、總結
至此,我們發現其實MVC的異常處理,真的很簡單,只需要在過濾器中全局注冊之后,然后重寫OnException的方法,實現邏輯即可。關鍵是在于項目中Ajax請求,需要用統一的封裝方法。
好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.cnblogs.com/xyb0226/p/9250334.html