欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

NET MVC全局异常处理(一)

程序员文章站 2022-03-27 12:01:34
[TOC] .NET MVC全局异常处理 一直知道有.NET有相关的配置,但没有实际做过,以为改下设定就可以,结果实际使用的时候还是遇到不少问题,所以要记录一下。 IIS配置 刚开始不想改程序代码,所以直接就想到了IIS里面的错误页配置配置,一开始反复测试,设置改了很多,但是没有效果,后来发现是静态 ......

.net mvc全局异常处理

一直知道有.net有相关的配置,但没有实际做过,以为改下设定就可以,结果实际使用的时候还是遇到不少问题,所以要记录一下。

iis配置

刚开始不想改程序代码,所以直接就想到了iis里面的错误页配置配置,一开始反复测试,设置改了很多,但是没有效果,后来发现是静态页的配置,还没有进入mvc的程序部分,所以对于.net mvc这种动态页是不生效的,应该使用.net错误页选项

静态错误页配置

静态页配置流程如下:

NET MVC全局异常处理(一)
NET MVC全局异常处理(一)
NET MVC全局异常处理(一)

如上图所示,iis中配置对错误的响应有三种方式,默认情况是第三个,本地访问显示详细错误信息,外部地址访问显示自定义页面,这样方便开发者调试,如果没有设置专门的错误页会使用iis自带的样式,也就是第二张图中的配置,根据路径我们可以找到这样一个文件夹,里面都是错误提示的静态页,对应不同的状态代码

NET MVC全局异常处理(一)

我们可以把iis设置为均使用自定义错误页看下效果,或者直接通过文件访问

NET MVC全局异常处理(一)
NET MVC全局异常处理(一)

上面那张是详细的静态404错误,可以看到会暴露我们系统路径,下面则是默认的自定义错误页

静态错误的默认页有相应的设置,看似可以修改,有“文件”、“执行url”、“重定向”三种,但是实际设置一下就会发现报错:锁定错误

NET MVC全局异常处理(一)

通过这个错误我们去搜索解决方法可以看到一些人说将web.config中的httperror节下的defaultpath解锁即可,但似乎这是iis7以前的设置,在iis10中并没有相应的选项,看到一些说明提到可能是官方使用了更加安全的管理机制,因为发现这边的配置是静态页相关,不符合我的需要,没有深入研究,如果一定要使用这种可以看看这篇博客,试试能否通过系统命令解决锁定的问题

win7 iis web.config节点锁定问题

.net错误页配置

.net错误页的设置与静态页差不多,除了入口不一样,配置的选项也不太相同,但是整体意思一样

NET MVC全局异常处理(一)
NET MVC全局异常处理(一)

可以看到这里要求是绝对url,所以实际使用起来应该是不太方便,所以没有找到太多相关资料。另外,需要web.conig中的customerror设为on,部分异常如500会自动跳转到mvc的默认错误页home/error

NET MVC全局异常处理(一)

使用iis的错误页处理虽然不用改代码,但是维护起来局限性很多,最终还是应该通过程序进行全局异常捕获

程序设置

通过程序控制的方法我想到两种,一个是使用全局配置文件global.asax中的application_error方法,另一个是使用mvc的过滤器,默认的四种过滤器中就包含异常过滤

全局异常配置

这种方法对于webform和mvc都是通用的,在asp.net中,只要网站程序抛出未捕获的异常都会触发application_error事件。

使用此方法一定要把globalfilter全局过滤器中的handleerrorattribute注册取消掉,也可以将配置文件中的customerrors节点关闭,否则http 500的错误将不会被application_error事件捕获。

NET MVC全局异常处理(一)
NET MVC全局异常处理(一)

捕获到异常之后我们可以很容易地跳转到静态页面

protected void application_error(object sender, eventargs e)
{
    exception exception = server.getlasterror();
    var httpstatuscode = (exception as httpexception)?.gethttpcode() ?? 700; //如果为空则走自定义
    var httpcontext = ((mvcapplication)sender).context;
    httpcontext.clearerror();

    switch (httpstatuscode)
    {
        case 404:
            httpcontext.response.redirect("~/error/404.htm");
            break;
        default:
            httpcontext.response.redirect("~/error/500.htm");
            break;
    }
}

在一般情况下我们也可以指向一个控制器

protected void application_error(object sender, eventargs e)
{
    exception exception = server.getlasterror();
    var httpstatuscode = (exception as httpexception)?.gethttpcode() ?? 700; //如果为空则走自定义
    var httpcontext = ((mvcapplication)sender).context;
    httpcontext.clearerror();

    var routedic = new routevaluedictionary
    {
        {"controller", "home"},
        { "action","error"}
    };
    httpcontext.response.redirecttoroute("default", routedic);
}

但是在实际的业务中遇到了一些http请求的问题,在处理一部分代码抛出的异常时会出现“服务器无法在已发送http标头之后······”这一系列异常,如“设置状态”、“追加标头”等,这个时候跳转要使用另一种写法

protected void application_error(object sender, eventargs e)
{
    server.clearerror();
    response.tryskipiiscustomerrors = true;
    var routedata = new routedata();
    icontroller controller = new homecontroller();
    routedata.values.add("controller", "home");
    routedata.values.add("action", "error");
    controller.execute(new requestcontext(new httpcontextwrapper(context), routedata));
    response.end();
}

这里要注意的一点是如果要使用area中的控制器不能写成routedata.values.add,而是使用datatokens

routedata.datatokens.add("area", "testarea");