当前位置: 移动技术网 > IT编程>开发语言>.net > ASP.NET MVC中异常Exception拦截的深入理解

ASP.NET MVC中异常Exception拦截的深入理解

2018年08月08日  | 移动技术网IT编程  | 我要评论

一、前言

由于客户端的环境不一致,有可能会造成我们预计不到的异常错误,所以在项目中,友好的异常信息提示,是非常重要的。在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请求,需要用统一的封装方法。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网