【问题标题】:ASP.NET MVC4 CustomErrors DefaultRedirect IgnoredASP.NET MVC4 CustomErrors DefaultRedirect 被忽略
【发布时间】:2013-07-12 06:55:18
【问题描述】:

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

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

我有一个带有 Index 方法和相应视图的错误控制器,但我仍然得到默认的 404 错误页面。我还尝试将我的defaultRedirect 设置为静态 html 文件,但无济于事。我尝试在&lt;customErrors&gt; 中添加特定于404 的错误处理,甚至尝试以编程方式修改路由,但都没有结果。我错过了什么?为什么 ASP 忽略了我的默认错误处理?

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

【问题讨论】:

  • 在 Stackoverflow 和其他网站上有很多关于自定义错误处理和一般错误处理的详细帖子:stackoverflow.com/questions/619895/…codeproject.com/Articles/422572/… 此外,一旦您设置了所有内容并且想要测试您的网站的安全性,您可以使用此站点:asafaweb.com 我建议您阅读这些内容并尝试提出最适合您的场景的内容。
  • 你安装了 Fiddler 吗?如果是,您提出的请求应该返回 404,您能告诉我们它实际上返回了什么吗?
  • 我已经查看过 SO、MSDN、codeproject 等。解决方案始终归结为为控制器中的异常设置 HandleErrorAttribute(它正在工作),并拥有所有其他异常(例如由用户点击错误地址引起的 404)使用 customErrors 的 defaultRedirect 属性进行处理,我已经这样做了。问题是它根本不起作用。对于 HandleError 未处理的事情,我仍然得到默认的 ASP 错误页面。
  • 我认为提琴手不会告诉我太多。我想要我的自定义异常页面,但我得到的是默认的 .net 404。例如,请参阅 therealmitchconnors-nat.azurewebsites.net/ggg(不存在的页面)。
  • 您的 CustomHandlerAttribute 代码是什么样的?对我来说它有效,只是我没有实现任何用于处理错误的自定义属性。

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


【解决方案1】:

这应该可行:

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。在FilterConfig类中注册HandleErrorAttribute作为全局动作过滤器如下

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");
    }
}

【讨论】:

  • 这应该可以工作,虽然我会使用 Application_Error 事件而不是 EndRequest,但我仍然希望通过让 customErrors 功能工作来解决这个问题。如果在赏金结束时没有答案能够帮助解决这个问题,我会接受你的答案。
【解决方案2】:

我有点跑题了。我认为这有点重要。

如果您注意上面突出显示的部分。我已经指定了动作过滤器的顺序。这基本上描述了Action Filter的执行顺序。这是当您通过控制器/动作方法实现多个动作过滤器时的情况

这张图片只是表明假设你有两个动作过滤器。 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. 异常过滤器

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

回到原来的查询

这对我有用。这非常简单,无需考虑 Web.Config 中的任何更改或在 Global.asax 文件中注册操作过滤器。

好的。所以,首先我要创建一个简单的动作过滤器。这将处理 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);
    }
}

现在您可以在控制器上以及仅在动作上实现此动作过滤器。示例:

希望这对你有帮助。

【讨论】:

  • 我认为在 Global.ajax 中的 Application_Error 中编写错误条件并重定向到要好得多。你不必在任何地方写任何东西。那@PKKG 呢?你怎么看。如果这符合标准?请给我一个建议。通过查看您的示例,我应该创建一个过滤器,并且我应该在任何地方使用该过滤器。但是,我发现只实现一个过程更容易,而没有其他地方可以编写任何代码。这是否违反任何 .NET 规则或其他什么?
  • 我的方法灵活吗?您想将此设置应用于特定的操作方法/特定控制器/多控制器吗?您想在执行某些特定的操作过滤器后应用此设置吗?您想在执行某些特定的 Action 方法后应用此设置吗?你还想要什么?
  • 这个很彻底,但是HandleErrorAttribute不能拦截404之类的……
  • @therealmitchconnors:你是对的。此代码中缺少 404 跟踪。
【解决方案3】:

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

在 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 指定的重定向。

考虑到我刚才所说的,defaultRedirecterror 标签很容易理解。错误标签用于指定特定状态代码的重定向。如果没有对应的错误标签,则使用 defaultRedirect。重定向 URL 可以指向服务器可以处理的任何内容,包括控制器操作。

MVC 层

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

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

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

在控制器/动作被解析后,MVC 层的错误处理开始,基于请求 URL。例如,不满足操作参数的请求在 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。

【讨论】:

    【解决方案4】:

    创建一个控制器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 页面。

    【讨论】:

    • 问题是 HandleError 只处理 HTTP500 错误,这意味着只在控制器内部抛出异常(并且该部分工作正常)。我需要处理一个用户试图点击我网站中不存在的随机页面......现在它只提供 ASP 的默认丑陋 404 页面。
    【解决方案5】:

    我不确定这个答案是否会对您有所帮助,但这是一种简单的方法...我将 error.html 放在 / 中,并为 web 配置中的自定义错误打开了模式,这非常有效...

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

    这个error.html是一个带有head和body的基本html页面..

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-08
        • 2013-02-26
        • 1970-01-01
        • 2012-05-17
        • 2020-01-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多