一、前言
由于客戶端的環(huán)境不一致,有可能會造成我們預(yù)計不到的異常錯誤,所以在項目中,友好的異常信息提示,是非常重要的。在asp.net mvc中實現(xiàn)異常屬性攔截也非常簡單,只需要繼承另一個類(System.Web.Mvc.FilterAttribute)和一個接口(System.Web.Mvc.IExceptionFilter),實現(xiàn)接口里面OnException方法,或者直接繼承Mvc 提供的類System.Web.Mvc.HandleErrorAttribute。
下面話不多說了,來一起看看詳細(xì)的介紹吧
二、實現(xiàn)關(guān)鍵邏輯
繼承System.Web.Mvc.HandleErrorAttribute,重寫了OnException方法,主要實現(xiàn)邏輯代碼如下:
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> /// 控制器方法中出現(xiàn)異常,會調(diào)用該方法捕獲異常 /// </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) { //友好錯誤提示,未授權(quán)錯誤提示,記錄警告日志 LogHelper.Warn(context.Exception.Message); } else { //異常錯誤, LogHelper.Error(context.Exception); ////TODO :寫入錯誤日志到數(shù)據(jù)庫 } } } |
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未授權(quán)異常,當(dāng)頁面未被授權(quán)訪問時,返回該異常,并攜帶有未授權(quán)的路徑地址。其他異常統(tǒng)一返回500錯誤,并攜帶異常信息。
三、異常處理
1.401 未授權(quán)錯誤
異常定義代碼:
1
2
3
4
5
6
7
8
9
10
|
/// <summary> /// 沒有被授權(quán)的異常 /// </summary> public class NoAuthorizeException : Exception { public NoAuthorizeException( string message) : base (message) { } } |
拋出異常代碼:
1
|
throw new NoAuthorizeException( "未授權(quán)" ); |
前端UI效果:
2.404 未找到頁面錯誤
MVC的404異常處理,有幾種方式,我們采用了在Global.asax全局請求函數(shù)中處理, 請查看以下代碼
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服務(wù)器內(nèi)部錯誤
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> /// 構(gòu)造函數(shù) /// 默認(rèn)顯示異常頁面 /// </summary> /// <param name="ex">異常對象</param> public ExceptionMessage(Exception ex) : this (ex, true ) { } /// <summary> /// 構(gòu)造函數(shù) /// </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> /// 消息內(nèi)容 /// </summary> public string Message { get ; set ; } /// <summary> /// 請求路徑 /// </summary> public string Path { get ; set ; } /// <summary> /// 程序集名稱 /// </summary> public string Assembly { get ; set ; } /// <summary> /// 異常參數(shù) /// </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> /// 服務(wù)器IP 端口 /// </summary> public string IP { get ; set ; } /// <summary> /// 客戶端瀏覽器標(biāo)識 /// </summary> public string UserAgent { get ; set ; } /// <summary> /// 是否顯示異常界面 /// </summary> public bool ShowException { get ; set ; } /// <summary> /// 異常發(fā)生時間 /// </summary> public string Time { get ; set ; } /// <summary> /// 異常發(fā)生方法 /// </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) { } } |
在異常攔截關(guān)鍵代碼中,我們發(fā)現(xiàn)友好異常(UserFriendlyException)其實是返回了一個結(jié)果對象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操作結(jié)果 /// </summary> public class AjaxResult { /// <summary> /// 獲取 Ajax操作結(jié)果類型 /// </summary> public ResultType type { get ; set ; } /// <summary> /// 獲取 Ajax操作結(jié)果編碼 /// </summary> public int errorcode { get ; set ; } /// <summary> /// 獲取 消息內(nèi)容 /// </summary> public string message { get ; set ; } /// <summary> /// 獲取 返回數(shù)據(jù) /// </summary> public object resultdata { get ; set ; } } /// <summary> /// 表示 ajax 操作結(jié)果類型的枚舉 /// </summary> public enum ResultType { /// <summary> /// 消息結(jié)果類型 /// </summary> info = 0, /// <summary> /// 成功結(jié)果類型 /// </summary> success = 1, /// <summary> /// 警告結(jié)果類型 /// </summary> warning = 2, /// <summary> /// 異常結(jié)果類型 /// </summary> error = 3 } |
四、Ajax請求異常時處理
在異常攔截的關(guān)鍵代碼中,我們有看到,如果是ajax請求時,是執(zhí)行不同的邏輯,這是因為ajax的請求,不能直接通過MVC的路由跳轉(zhuǎn),在請求時必須返回結(jié)果內(nèi)容
然后在前端ajax的方法中,統(tǒng)一處理返回的錯誤,以下是我們項目中用到的ajax封裝,對異常錯誤,進行了統(tǒng)一處理。
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 通信異常的時候調(diào)用此方法 $.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參數(shù), callback回調(diào)函數(shù) beforeSend 請求之前回調(diào)函數(shù), complete 請求完成之后回調(diào)函數(shù) * 考慮到get請求一般將參數(shù)與url拼接一起傳遞,所以將param參數(shù)放置最后 * 返回AjaxResult結(jié)果對象 */ $.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參數(shù) * 返回實體數(shù)據(jù)對象 */ $.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參數(shù), callback回調(diào)函數(shù) beforeSend 請求之前回調(diào)函數(shù), complete 請求完成之后回調(diào)函數(shù) * 返回AjaxResult結(jié)果對象 */ $.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參數(shù), callback回調(diào)函數(shù) * 返回實體數(shù)據(jù)對象 */ $.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參數(shù), callback回調(diào)函數(shù) * 返回實體數(shù)據(jù)對象 */ $.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); |
五、總結(jié)
至此,我們發(fā)現(xiàn)其實MVC的異常處理,真的很簡單,只需要在過濾器中全局注冊之后,然后重寫OnException的方法,實現(xiàn)邏輯即可。關(guān)鍵是在于項目中Ajax請求,需要用統(tǒng)一的封裝方法。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://www.cnblogs.com/xyb0226/p/9250334.html