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请求,需要用统一的封装方法。

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

以上是 ASP.NET MVC中异常Exception拦截的深入理解 的全部内容, 来源链接: utcz.com/z/336669.html

回到顶部