忽略ASP.NET MVC4 CustomErrors DefaultRedirect

时间:2013-07-14 05:32:19

标签: asp.net-mvc asp.net-mvc-3 asp.net-mvc-4

我有一个MVC 4应用程序,使用自定义HandleErrorAttribute来处理自定义异常。我想拦截默认的404和其他非500错误页面,并用更具吸引力的东西替换它们。为此,我将以下内容添加到我的Web.config:

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

我有一个带有索引方法和相应视图的错误控制器,但我仍然得到默认的404错误页面。我也尝试将我的defaultRedirect设置为静态html文件无效。我已经尝试在<customErrors>内添加特定于404的错误处理,我甚至尝试以编程方式修改路由,所有这些都没有结果。我错过了什么?为什么ASP忽略了我的默认错误处理?

注意:我之前注意到我无法在本地测试我的CustomHandleErrorAttribute,即使使用<customErrors mode="On"也是如此。当我在我的服务器上从我的开发盒中点击它时它确实有效...不确定这是否相关。 This guy遇到了同样的问题。

6 个答案:

答案 0 :(得分:14)

这应该有效:

<强> 1。 Web.Config中

<customErrors mode="On"
   defaultRedirect="~/Views/Shared/Error.cshtml">

  <error statusCode="403"
    redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />

  <error statusCode="404"
    redirect="~/Views/Shared/FileNotFound.cshtml" />

</customErrors>

<强> 2。将FilterleErrorAttribute注册为FilterConfig类中的全局操作过滤器,如下所示

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }

如果那不起作用,请尝试通过检查状态代码(例如Global.asax中的以下内容)来转移响应:至少它必须有效。

void Application_EndRequest(object sender, EventArgs e)
{
    if (Response.StatusCode == 401)
    {
        Response.ClearContent();
        Server.Transfer("~/Views/Shared/UnauthorizedAccess.cshtml");
    }
}

答案 1 :(得分:6)

我的主题很少。我认为解释这一点非常重要。

enter image description here

如果你注意上面突出显示的部分。我已经指定了Action Filter的顺序。这基本上描述了Action Filter的执行顺序。当您通过Controller / Action Method

实现多个Action Filters时,就会出现这种情况

enter image description here

这张图片只是表明你有两个动作过滤器。 OnActionExecution将开始执行优先级,OnActionExecuted将从底部开始到顶部。这意味着如果OnActionExecuted动作过滤器具有最高顺序将首先执行,而在OnActionExecuting动作过滤器具有最低顺序将首先执行。以下示例。

public class Filter1 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

//执行将从这里开始 - 1

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {

//执行将移至此处 - 5

        base.OnActionExecuted(filterContext);
    }
}

public class Filter2 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

//执行将移至此处 - 2

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {

//执行将移至此处 - 4

        base.OnActionExecuted(filterContext);
    }
}

[HandleError]
public class HomeController : Controller
{
    [Filter1(Order = 1)]
    [Filter2(Order = 2)]
    public ActionResult Index()
    {

//执行将移至此处 - 3

        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }
}

您可能已经意识到MVC框架中有不同类型的过滤器。它们列在下面。

  1. 授权过滤器

  2. 动作过滤器

  3. 响应/结果过滤器

  4. 异常过滤器

  5. 在每个过滤器中,您可以指定Order属性。这基本上描述了动作过滤器的执行顺序。

    返回原始查询

    这适合我。这非常简单,无需考虑Web.Config中的任何更改或在Global.asax文件中注册Action Filter。

    确定。所以,首先我要创建一个简单的动作过滤器。这将处理Ajax和非Ajax请求。

    public class MyCustomErrorAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        {
            filterContext.ExceptionHandled = true;
            var debugModeMsg = filterContext.HttpContext.IsDebuggingEnabled
                                   ? filterContext.Exception.Message +
                                     "\n" +
                                     filterContext.Exception.StackTrace
                                   : "Your error message";
    

    // 当您需要处理Ajax请求

    时就是这种情况
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new JsonResult
                {
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                    Data = new
                    {
                        error = true,
                        message = debugModeMsg
                    }
                };
            }
    

    // 处理非Ajax请求时的情况

            else
            {
                var routeData = new RouteData();
                routeData.Values["controller"] = "Error";
                routeData.Values["action"] = "Error";
                routeData.DataTokens["area"] = "app";
                routeData.Values["exception"] = debugModeMsg;
                IController errorsController = new ErrorController();
                var exception = HttpContext.Current.Server.GetLastError();
                var httpException = exception as HttpException;
                if (httpException != null)
                {
                    Response.StatusCode = httpException.GetHttpCode();
                    switch (System.Web.HttpContext.Current.Response.StatusCode)
                    {
                        case 404:
                            routeData.Values["action"] = "Http404";
                            break;
                    }
                }
    
                var rc = new RequestContext
                             (
                                 new HttpContextWrapper(HttpContext.Current),
                                 routeData
                             );
                errorsController.Execute(rc);
            }
            base.OnException(filterContext);
        }
    }
    

    现在,您可以在Controller上以及仅在Action上实现此Action Filter。示例:

    enter image description here

    希望这对你有所帮助。

答案 2 :(得分:0)

创建一个Controller ErrorController。

 public class ErrorController : Controller
    {
        //
        // GET: /Error/

        public ActionResult Index()
        {
            return View();
        }
}

为操作创建索引视图。

<。>在Web.config中

<customErrors mode="On">
      <error statusCode="404" redirect="Error/Index"/>
</customErrors>

当您处理代码/逻辑中的错误时

[HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start application.";

            return View("Index2");
        }
}

[HandleError]属性 - 将重定向到共享文件夹内的Error.cshtml页面。

答案 3 :(得分:0)

我不确定这个答案是否会对您有所帮助,但这只是一个简单的方法...我将/中的error.html设置为on并在Web配置中将自定义错误设置为on,这非常有效......

  <system.web>
    <customErrors defaultRedirect="~/Error.html" mode="On" />
  </system.web>

这个error.html是一个带头部和身体的基本html页面。

答案 4 :(得分:0)

在调查此问题后,我想分享我的知识。欢迎任何有助于改善我的陈述的评论。

在ASP.NET MVC中,三层按以下顺序处理HTTP请求(响应以相反的顺序进行传输):

  1. IIS(HTTP层)

  2. ASP.NET(服务器层)

  3. 控制器(MVC层)

所有这些层都有错误处理,但是每个层的处理方式不同。我将从IIS开始。

IIS层

IIS如何处理错误的最简单示例是使用浏览器从服务器请求不存在的.html文件。地址应如下所示:

http://localhost:50123/this_does_not_exist.html

注意浏览器选项卡的标题,例如: IIS 10.0详细错误-404.0-未找到

ASP.NET层

当IIS收到HTTP请求时,如果URL以.aspx结尾,则将其转发到ASP.NET,因为它已注册为处理此扩展名。 ASP.NET如何处理错误的最简单示例是使用浏览器从服务器请求不存在的.aspx文件。地址应如下所示:

http://localhost:50123/this_does_not_exist.aspx

请注意页面底部显示的版本信息,表示ASP.NET的版本。

customErrors 标签最初是为ASP.NET创建的。当响应由ASP.NET内部代码创建时,它仅有效。这意味着它不会影响从应用程序代码创建的响应。此外,如果ASP.NET返回的响应不包含任何内容,并且具有错误状态代码(4xx或5xx),则IIS将根据状态代码替换响应。我将提供一些示例。

如果Page_Load方法包含Response.StatusCode = 404,则内容将正常显示。如果添加了附加代码Response.SuppressContent = true,则IIS以与请求“ this_does_not_exist.html”时相同的方式进行干预并处理404错误。没有内容和状态代码2xx的ASP.NET响应不受影响。

当ASP.NET无法使用应用程序代码完成请求时,它将使用内部代码处理该请求。请参阅以下示例。

如果无法解析URL,ASP.NET会自行生成一个响应。默认情况下,它将使用包含有关问题的详细信息的HTML正文创建404响应。可以使用customErrors来创建302(重定向)响应。但是,访问导致ASP.NET返回404响应的有效URL不会触发customErrors指定的重定向。

当ASP.NET从应用程序代码捕获异常时,也会发生同样的情况。默认情况下,它使用HTML主体创建500响应,其中包含有关引起异常的源代码的详细信息。同样,customErrors可代替生成302(重定向)响应。但是,从应用程序代码创建500响应不会触发customErrors指定的重定向。

defaultRedirect error 标签非常直观,可以理解我刚才所说的内容。错误标记用于为特定状态码指定重定向。如果没有相应的错误标记,则将使用defaultRedirect。重定向URL可以指向服务器可以处理的任何内容,包括控制器操作。

MVC层

使用ASP.NET MVC,事情变得更加复杂。首先,可能有两个“ Web.config”文件,一个在根目录中,一个在Views文件夹中。我想指出,Views的默认“ Web.config”对此线程有两件有趣的事情:

  • 它禁用处理.cshtml文件的URL(webpages:Enabled设置为false)
  • 它防止直接访问Views文件夹(BlockViewHandler)中的任何内容

对于ASP.NET MVC,可以将 HandleErrorAttribute 添加到GlobalFilters,这还考虑了 mode 属性的值。来自根“ Web.config”的customErrors标记。更具体地说,当该设置为On时,它将启用MVC层中针对控制器/操作代码中未捕获的异常的错误处理。而不是将它们转发到ASP.NET,它默认情况下呈现Views / Shared / Error.cshtml。可以通过设置HandleErrorAttribute的View属性来更改。

基于请求URL,在控制器/操作解决后,MVC层开始进行错误处理。例如,在MVC层处理不满足操作参数的请求。但是,如果POST请求没有匹配的控制器/动作可以处理POST,则该错误将在ASP.NET层处理。

我已使用ASP.NET MVC 5进行测试。 IIS和IIS Express在错误处理方面似乎没有什么区别。

答案

我能想到为什么不为非500状态代码不考虑customErrors的唯一原因是因为它们是使用HttpStatusCodeResponse创建的。在这种情况下,响应是由应用程序代码创建的,而不是由ASP.NET处理,而是由IIS处理。此时,配置备用页面是没有意义的。这是重现此行为的示例代码:

public ActionResult Unhandled404Error()
{
    return new HttpStatusCodeResult(HttpStatusCode.NotFound);
}

在这种情况下,我建议实现一个ActionFilterAttribute,它将覆盖OnResultExecuted并执行以下操作:

int statusCode = filterContext.HttpContext.Response.StatusCode;
if(statusCode >= 400)
{
    filterContext.HttpContext.Response.Clear();
    filterContext.HttpContext.Response.Redirect("/Home/Index");
}

已实现的ActionFilterAttribute应该添加到GlobalFilters。

答案 5 :(得分:0)

对我来说,它可以删除默认的Error.cshtml文件,现在它将使用Web.config中的自定义Error defaultRedirect页面。