/*十有三博客*/
  • 首页
  • 关于本站
  • 网站地图
  • RSS订阅

ASP.NET MVC 5 学习笔记:使用HandleErrorAttribute处理异常

2015-11-23 十有三 2 浏览:2万+ .NET技术 ASP.NET MVC

ASP.NET MVC 默认提供了一个异常过滤器HandleError特性,使用该特性可以极为方便的捕捉并处理控制器和操作抛出的异常,也可以将此特性注册为全局异常过滤器从而捕捉项目中所有Action方法抛出的异常。如果想要简单的消灭错误黄页(错误详细页),使用HandlerErrorAttribute是不错的选择!

本文演示项目下载地址:GlobalExceptionHandle-By-HandleErrorAttribute.zip,项目使用的VS2013和ASP.NET MVC 5框架,项目中也拷贝了HandleErrorAttribute的源码以供参考。

HandleErrorAttribute初步使用

使用HandleErrorAttribute处理异常很简单,首先要开启Web.config配置文件中的自定义错误,因为HandleError特性是依赖自定义错误的,customErrors的Mode必须要设置为On或RemoteOnly:

<customErrors mode="On" defaultRedirect="~/Error/Index"></customErrors>

到了这里基本上就成功启用了异常过滤器,因为VS2013新建的ASP.NET MVC 5项目默认将HandleError注册为全局异常过滤器,只要项目中的控制器和操作方法有抛出异常,默认就会被HandleError特性捕获,从而跳转到默认的错误详细页面~/Views/Shared/Error.cshtml。打开项目下App_Start的FilterConfig类(全局过滤器配置类),可以看到已经被注册的HandleErrorAttribute类:

FilterConfig类中全局注册了HandleErrorAttribute类

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        //如果要更改默认的错误视图,需要设置View属性
        //filters.Add(new HandleErrorAttribute() { View = "~/Views/Error/CustomHttpError.cshtml" });
    }
}

备注:关于FilterConfig类,如果不清楚的朋友可以看这篇文章:ASP.NET MVC 5 学习笔记之FilterConfig类

这里可以动态抛出一些异常来进行测试,在默认的Home控制器下复制如下代码:

public class HomeController : Controller
{                
    public ActionResult Index()
    {
        return View();
    }
    /// <summary>
    /// 抛出HTTP 500
    /// </summary>
    /// <returns></returns>        
    public ActionResult ThrowHttp500()
    {
        throw new HttpException(500, "服务器错误");
    }
    /// <summary>
    /// 抛出HTTP 404
    /// </summary>
    /// <returns></returns>
    public ActionResult ThrowHttp404()
    {
        throw new HttpException(404, "页面未找到");
    }

    /// <summary>
    /// 抛出未引用对象异常
    /// ,此处单独使用HandleError特性
    /// ,并指定异常类型及响应视图
    /// </summary>
    /// <returns></returns>
    [HandleError(ExceptionType = typeof(NullReferenceException),View="CustomError")]
    public ActionResult ThrowNullReferenceException()
    {
        throw new NullReferenceException();
    }
    /// <summary>
    /// 引发输入字符串的格式不正确异常
    /// ,此处指定了响应的错误页面
    /// ,由于是不同的控制器所以要完整的相对路径
    /// </summary>
    /// <returns></returns>
    [HandleError(View = "~/Views/Error/CustomHttpError.cshtml")]
    public ActionResult ThrowFormatException()
    {
        string str = "";
        int count = Convert.ToInt32(str);
        return View("Index");
    }
}

备注:记得添加对应的错误视图页和控制器,建议直接下载演示项目。

首页操作演示界面如下:

ASP.NET MVC 5 中HandleErrorAttribute类演示项目

灵活运用HandleError特性

HandleError特性提供了许多属性,我们可以更加灵活的处理项目中抛出的异常。文档截图:

HandleError的属性截图

一般来说最常用的属性是View,Order和ExceptionType,至于AllowMultiple属性,除非业务有特殊要求,不然默认都是允许的。

自定义错误信息页面

先说View属性,设置此属性就可以自定义要跳转的错误视图页,否则一旦有异常抛出,默认会跳转到Error.cshtml这个页面,该页面路径为:~/Views/Shared/Error.cshtml。

设置View属性一定要注意路径问题,如果跳转的异常信息页面属于其他控制器,即控制器路由地址不同,那么一定要用完整的相对路径,否则引发二次异常:未找到视图**或其母版视图,或没有视图引擎支持搜索的位置。,比如下面代码(此声明是在Home控制器中,但是要跳转的错误页面则是在Error控制器中):

[HandleError(View = "~/Views/Error/CustomHttpError.cshtml")]

如果跳转的异常页面是属于当前控制器,或者是属于公共视图页的,就可以直接设置视图名称,代码如下:

[HandleError(View="CustomError")]

备注:CustomError是一个公共的自定义错误信息视图页面,另外公共视图页都是放在~/Views/Shared/这个路径下。

如果不想使用默认的Error.cshtml,想将所有的异常响应跳转到自定义的异常信息视图页,那么只要通过全局过滤器配置类FilterConfig,在注册HandleErrorAttribute时设置View属性即可,代码如下:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {        
        filters.Add(new HandleErrorAttribute() { View = "~/Views/Error/CustomHttpError.cshtml" });
    }
}

同样要注意视图路径问题,其实这里最好使用公共视图页。这里额外说下Master属性,此属性可以指定要跳转的视图页的母板。基本上很少用,除非项目业务有需要,比如动态控制母板什么的,不然的话都是在视图中直接设置好的。

获取详细的异常信息

HandleErrorInfo类是HandleErrorAttribute默认提供的错误信息实体

HandleErrorInfo类属性截图

可以通过此类获取引发异常的控制器和操作方法名称,以及通过获取Exception对象获取详细的错误内容。只要在对应的视图页中声明此类即可,演示代码:

@model System.Web.Mvc.HandleErrorInfo
@{
    ViewBag.Title = "自定义的公共错误页面";
}
@section styles{
    <style type="text/css">
        p {padding: 10px;}
    </style>
}
<h2>自定义的公共错误页面</h2>

@if (Model != null)
{
    <p class="bg-danger text-danger">
        异常类型:@Model.Exception.GetType().Name
    </p>
    <p class="bg-danger text-danger">
        触发异常的控制器:@Model.ControllerName
    </p>
    <p class="bg-danger text-danger">
        触发异常的操作方法:@Model.ActionName
    </p>
    <p class="bg-danger text-danger">
        错误信息:@Model.Exception.Message
    </p>
    <p class="bg-info text-info">
        页面路径:~/Views/Shared/CustomError.cshtml        
    </p>
}

演示的视图截图:

ASP.NET MVC 5演示项目截图

针对异常类型

ExceptionType属性可以让HandleError针对某种异常类型做出处理,可以搭配View属性来使用,便于跳转到不同的异常信息页面,演示代码:

[HandleError(ExceptionType = typeof(NullReferenceException),View="CustomError")]

上面代码一旦捕获到NullReferenceException异常,就会跳转到CustomError视图页。注意一点,ExceptionType需要使用typeof转化异常类型。

关于过滤器的执行顺序

如果声明了多个HandleError异常过滤器,就需要使用到Order属性设置运行顺序。Order属性默认值为-1,也是最高优先级,正常来说整数值越大优先级越低,但是由于HandleError是继承于IExceptionFilter接口,所以优先级顺序是相反的,也就是说整数值越大,优先级也就越大,这里分享下官方文档的资料:

在 ASP.NET MVC 版本 1 和 2 中,OnException(ExceptionContext) 筛选器以正向顺序运行。 在 ASP.NET MVC 版本 3 及更高版本中,此顺序已反转。 ASP.NET MVC 中的异常筛选器的行为类似于 .NET Framework 中的异常处理程序,后者由内而外展开。

这里有两点需要注意:

  1. 如果Order属性小于-1会抛出异常,所以只能设置大于等于-1的整数值。
  2. 如果没有设置Order属性,由于默认值都是-1(即最高优先级),会导致过滤器的执行顺序变成无序随机。

使用HandleErrorAttribute的一些注意事项

虽然HandleError特性使用起来很简单,但是依然有很多需要注意很多地方,下面会详细的分析特性的局限性和一些缺点。

依赖于ASP.NET的自定义错误模块

如果customErrors mode="Off",则HandleError则无效,不对任何异常进行处理。

源码:

// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
    return;
}

从源码中可以看到,当HttpContext.IsCustomErrorEnabled属性为false时(即未启用自定义错误),就不进行任何处理,直接return停止流程。

只能处理500服务器错误

像HTPP 404、401、503等错误代码,都是不处理的。源码可以很直接看出,只要不是500错误就直接return返回:

// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500)
{
    return;
}

因此除了500错误,其他错误只能通过自定义错误模块配置响应的页面:

<customErrors mode="On" defaultRedirect="~/Error/Index">
  <error redirect="~/Error/NotFound" statusCode="404"/>
  <error redirect="~/Error/NotFound" statusCode="400"/>
</customErrors>

只能跳转到视图

HandleError只提供了一个View属性,所以响应的异常信息页也只能是视图,其他的静态页面或者.aspx之类的都是无法进行跳转的,可以使用自定义异常过滤器实现此功能。

跳转的原理是通过设置ExceptionContext.Result属性:

filterContext.Result = new ViewResult
{
    ViewName = View,
    MasterName = Master,
    ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
    TempData = filterContext.Controller.TempData
};

不利于SEO搜索引擎优化

这里涉及到部分SEO的知识,简单来说错误页面是使用302跳转,因此对搜索引擎抓取网站内容并不友好。如果是互联网行业的网站,考虑到SEO的问题还是不要用HandleErrorAttribute处理异常,类似企业系统的网站倒是十分适合使用。

至于应对的方法可以参考此文:ASP.NET MVC全局异常处理和捕获的思路

同时声明多个HandleError的效果

多个HandleError最终只有一个过滤器会执行,其他的过滤器会自动停止执行,至于是哪个过滤器执行则是依靠Order属性进行优先级确定,原理是使用ExceptionContext.ExceptionHandled属性进行判断,一旦该属性为true则表示当前抛出的异常已经被其他过滤器处理了,直接return停止余下的流程。

高级进阶之继承HandleErrorAttribute实现自定义功能

通过继承HandleErrorAttribute类创建新的异常处理特性,可以使我们扩展更多的自定义功能,比如记录错误日志、发送错误信息邮件等。继承后只要重写此类中的OnException方法即可实现功能,演示代码如下:

public class CustomHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        /* 调用基类的OnException方法,实现基础的功能。
        * 如果要完全的自定义,就不需要调用基类的方法
        */
        base.OnException(filterContext);
            
        /* 此处可进行记录错误日志,发送错误通知等操作
        * 通过Exception对象和HttpException对象可获取相关异常信息。
        * Exception exception = filterContext.Exception;
        * HttpException httpException = new HttpException(null, exception);
        */
    }
}

调用基类的OnException方法依然要注意HandleErrorAttribute类的一些限制,除非不使用基类的方法。继承后的新特性使用方法和HandleError一样,依然可以全局注册和局部使用。

如果需要完全自定义异常处理的功能,建议直接继承IExceptionFilter接口以实现功能,可以参考这篇文章:ASP.NET MVC实现IExceptionFilter接口编写自定义异常处理过滤器


参考资料分享

  1. HandleErrorAttribute源码
  2. HandleErrorAttribute类
  3. GlobalFilterCollection类
  4. Exception对象

作者:十有三

出处:https://shiyousan.com/post/635838881238204198

版权声明:本文采用知识共享许可协议:署名-相同方式共享 4.0 国际(CC BY-SA 4.0)。欢迎转载本文,转载请声明出处或保留此段声明。


  • 上一篇: ASP.NET MVC 5 学习笔记之FilterConfig类
  • 下一篇: ASP.NET MVC全局异常处理和捕获的思路

相关文章
  • ASP.NET MVC:此请求的查询字符串的长度超过配置的maxQueryStringLength值
  • ASP.NET MVC网站发布后出现“/”应用程序中的服务器错误。
  • ASP.NET MVC 5 学习笔记之FilterConfig类
  • (译)在 ASP.NET中使用 XML-RPC 进行ping
  • Discuz!NT 图像因存在错误而无法显示
  • 解决IIS ASP.NET 网站发布后出现错误 Unable to connect to any of the specified MySQL hosts

文章分类

.NET技术 123 数据库 24 Web前端 21 网站建设运维 37 操作系统与应用 66 程序猿日常 11 开发工具 12 其他随笔 13

文章标签

ASP.NET ASP.NET MVC C# CSS HTML IIS Javascript Linux MongoDB MySql SQL SQL Server Visual Studio Windows系统 版本控制系统 插件工具 服务器 搞笑娱乐 好文分享 软件应用 生活知识 手机问题 随笔 网络知识 网站设计优化 网站维护 养生保健 异常处理 硬件设备 游戏攻略

热门文章

  • IIS8如何安装和使用URL重写工具-URL Rewrite
  • 林蛋大与楚中天,朱肚皮与朱月坡
  • 解决IE11安装升级失败和在安装前需要更新的问题
  • Windows Server 2012无法安装 .NET3.5-安装角色或功能失败,找不到源文件
  • VS重构重命名的快捷键

推荐文章

  • ASP.NET MVC Compare ErrorMessage 无效导致无法显示自定义错误信息
  • ASP.NET 动态输出404 HTTP状态代码
  • ASP.NET MVC中MvcHtmlString类的两个疑问:是什么以及怎么使用?
  • 安卓onenote出现错误代码80a00010导致无法登陆
  • 设置Windows10的微软拼音输入法默认为英文

友情链接

  • 码友网

知识共享许可协议 CC BY-SA 4.0本站作品采用知识共享许可协议:署名-相同方式共享 4.0 国际(CC BY-SA 4.0)。
闽ICP备15003702号
闽公网安备 35020302035102号