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

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

(0)

相关推荐

  • 基于SpringMVC的全局异常处理器介绍

    近几天又温习了一下SpringMVC的运行机制以及原理 我理解的springmvc,是设计模式MVC中C层,也就是Controller(控制)层,常用的注解有@Controller.@RequestMapping.@Autowared.@Component,今天呢,我所要写的是SpringMVC的全局异常处理器,关联的接口有HandlerExceptionResolver(Eclipse用户可以按Ctrl+Shift+T进行搜索该接口),什么是全局异常处理器?为什么要用它呢? 在企业开发中,各种

  • 详解使用Spring MVC统一异常处理实战

    1 描述 在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大. 那么,能不能将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护?答案是肯定的.下面将介绍使用Spring MVC统一处理异常的解决和实现过程. 2 分析 Spring MVC处理异常有3种方

  • ASP.NET MVC下基于异常处理的完整解决方案总结

    EntLib的异常处理应用块(Exception Handling Application Block)是一个不错的异常处理框架,它使我们可以采用配置的方式来定义异常处理策略.而ASP.NET MVC是一个极具可扩展开发框架,在这篇文章中我将通过它的扩展实现与EntLib的集成,并提供一个完整的解决异常处理解决方案. 一.基本异常处理策略 我们首先来讨论我们的解决方案具体采用的异常处理策略: 对于执行Controller的某个Action方法抛出的异常,我们会按照指定配置策略进行处理.我们可以采

  • Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码

    1.用户登录 验证用户是否登录成功步骤直接忽略,用户登录成功后怎么保存当前用户登录信息(session,cookie),本文介绍的是身份验证(其实就是基于cookie)的,下面看看代码. 引入命名空间 using System.Web.Security; 复制代码 代码如下: Users ModelUser = new Users() { ID = 10000, Name = UserName, UserName = UserName, PassWord = PassWord, Roles =

  • ASP.NET mvc异常处理的方法示例介绍

    1.首先常见保存异常的类(就是将异常信息写入到文件中去) 复制代码 代码如下: public class LogManager { private string logFilePath = string.Empty; public LogManager(string logFilePath) { this.logFilePath = logFilePath; FileInfo file = new FileInfo(logFilePath); if (!file.Exists) { file.C

  • springboot springmvc抛出全局异常的解决方法

    springboot中抛出异常,springboot自带的是springmvc框架,这个就不多说了. springmvc统一异常解决方法这里要说明的是.只是结合了springboot的使用而已.直接上代码,有效有用的才是ok. 1.定义异常捕获 package com.example.rest.error; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.Exce

  • ASP.NET MVC异常处理模块详解

    一.前言 异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示.记录错误信息,更重要的是不破坏正常的数据和影响系统运行.异常处理应该是一个横切点,所谓横切点就是各个部分都会使用到它,无论是分层中的哪一个层,还是具体的哪个业务逻辑模块,所关注的都是一样的.所以,横切关注点我们会统一在一个地方进行处理.无论是MVC还是WebForm都提供了这样实现,让我们可以集中处理异常. 在MVC中,在FilterConfig中,已经默认帮我们注册了一个HandleErrorAttr

  • asp.net core MVC 全局过滤器之ExceptionFilter过滤器(1)

    本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter过滤器(一) asp.net core MVC 过滤器之ActionFilter过滤器(二) asp.net core MVC 过滤器之ResultFilter过滤器(三) asp.net core MVC 过滤器之ResourceFilter过滤器(四) asp.net core MVC 过滤器之AuthorizationFilter过

  • ASP.NET MVC中异常处理&自定义错误页详析

    一.应用场景 对于B/S应用程序,在部署到正式环境运行的过程中,很有可能出现一些在前期测试过程中没有发现的一些异常或者错误,或者说只有在特定条件满足时才会发生的一些异常,对于使用ASP.NET MVC开发的应用程序站点,在部署到IIS上后,如果开发人员未对程序进行错误处理,那么一旦程序出现未处理的错误或异常,用户将看到一个让人感到及其困惑的错误堆栈跟踪页面,使得站点的用户体验下降,从程序的角度上来说,不做自定义错误处理也不利于程序出问题时的根源查找,因为很多时候有些错误只在特定条件下满足时才重现

  • MVC异常处理详解

    在日常开发中,我们会去捕捉很多的异常,来进行处理,通常我们的方法就是,在需要进行异常处理的地方加上 try catch 块,但是,如果需要异常处理的地方很多,那么,就会频繁的去写try catch 块,对于我们天生'懒惰' 的程序员来说,总想找一个捷径.于是,就会有全局异常处理,那么,今天,我们就来看看在MVC中怎么进行全局异常处理的. 一.MVC框架自己的全局异常处理 在MVC中,框架已经给我们一共了一套全局异常处理的特性类HandleErrorAttribute类.我们可以在MVC中的App

随机推荐