ASP.NET MVC中异常Exception拦截的深入理解
一、前言
由于客户端的环境不一致,有可能会造成我们预计不到的异常错误,所以在项目中,友好的异常信息提示,是非常重要的。在asp.net mvc中实现异常属性拦截也非常简单,只需要继承另一个类(system.web.mvc.filterattribute)和一个接口(system.web.mvc.iexceptionfilter),实现接口里面onexception方法,或者直接继承mvc 提供的类system.web.mvc.handleerrorattribute。
下面话不多说了,来一起看看详细的介绍吧
二、实现关键逻辑
继承system.web.mvc.handleerrorattribute,重写了onexception方法,主要实现逻辑代码如下:
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 过滤器全局注册异常拦截:
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 未授权错误
异常定义代码:
/// <summary> /// 没有被授权的异常 /// </summary> public class noauthorizeexception : exception { public noauthorizeexception(string message) : base(message) { } }
抛出异常代码:
throw new noauthorizeexception("未授权");
前端ui效果:
2.404 未找到页面错误
mvc的404异常处理,有几种方式,我们采用了在global.asax全局请求函数中处理, 请查看以下代码
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异常错误抛出的异常信息对象定义:
/// <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; } }
抛出异常代码:
throw new exception("出错了");
前端ui效果:
4. userfriendlyexception 友好异常
异常定义代码:
/// <summary> /// 用户友好异常 /// </summary> public class userfriendlyexception : exception { public userfriendlyexception(string message) : base(message) { } }
在异常拦截关键代码中,我们发现友好异常(userfriendlyexception)其实是返回了一个结果对象ajaxresult,
ajaxresult对象的定义:
/// <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封装,对异常错误,进行了统一处理。
(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请求,需要用统一的封装方法。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: SQL Server函数之空值处理
推荐阅读
-
夯实Java基础系列10:深入理解Java中的异常体系
-
夯实Java基础系列10:深入理解Java中的异常体系
-
ASP.NET MVC中异常Exception拦截的深入理解
-
ASP.NET Core中实现全局异常拦截的完整步骤
-
关于ASP.NET MVC中Form Authentication与Windows Authentication的简单理解
-
asp.net mvc core管道及拦截器的理解
-
深入理解Asp.Net中WebForm的生命周期
-
深入理解php中error与exception的区别
-
深入理解php中error与exception的区别
-
深入理解MVC中的时间js格式化_javascript技巧