【问题标题】:ASP.NET MVC - How to throw a 404 page similar to that on StackOverflowASP.NET MVC - 如何抛出类似于 StackOverflow 上的 404 页面
【发布时间】:2011-03-26 18:19:44
【问题描述】:

我目前有一个继承自 System.Web.Mvc.Controller 的 BaseController 类。在该课程中,我有 HandleError 属性,可将用户重定向到“500 - 糟糕,我们搞砸了”页面。这目前正在按预期工作。

这行得通

<HandleError()> _
Public Class BaseController : Inherits System.Web.Mvc.Controller

''# do stuff
End Class

我的 404 页面也以 Per-ActionResult 为基础工作,它再次按预期工作。

这行得通

    Function Details(ByVal id As Integer) As ActionResult
        Dim user As Domain.User = UserService.GetUserByID(id)

        If Not user Is Nothing Then
            Dim userviewmodel As Domain.UserViewModel = New Domain.UserViewModel(user)
            Return View(userviewmodel)
        Else
            ''# Because of RESTful URL's, some people will want to "hunt around"
            ''# for other users by entering numbers into the address.  We need to
            ''# gracefully redirect them to a not found page if the user doesn't
            ''# exist.
            Response.StatusCode = CInt(HttpStatusCode.NotFound)
            Return View("NotFound")
        End If

    End Function

同样,这很好用。如果用户输入http://example.com/user/999 之类的内容(其中用户ID 999 不存在),他们将看到相应的404 页面,但URL 不会改变(他们不会被重定向到错误页面)。

我无法实现这个想法

这就是我遇到问题的地方。如果用户输入http://example.com/asdf-,他们会被跳转到通用 404 页面。我想要做的是保留 URL(即:不重定向到任何其他页面),而只是显示“NotFound”视图并将HttpStatusCode.NotFound 推送到客户端。

例如,只需访问https://stackoverflow.com/asdf,您将看到自定义 404 页面并看到完整的 URL。

显然我错过了一些东西,但我无法弄清楚。由于“asdf”实际上并不指向任何控制器,因此我的基本控制器类没有启动,所以我无法在其中的“HandleError”过滤器中执行此操作。

提前感谢您的帮助。

注意:我绝对不想将用户重定向到 404 页面。我希望他们留在现有的 URL,我希望 MVC 将 404 VIEW 推送给用户。

编辑:

我也尝试了以下方法,但无济于事。

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.RouteExistingFiles = False
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
    routes.IgnoreRoute("Assets/{*pathInfo}")
    routes.IgnoreRoute("{*robotstxt}", New With {.robotstxt = "(.*/)?robots.txt(/.*)?"})

    routes.AddCombresRoute("Combres")

    ''# MapRoute allows for a dynamic UserDetails ID
    routes.MapRouteLowercase("UserProfile", _
        "Users/{id}/{slug}", _
        New With {.controller = "Users", .action = "Details", .slug = UrlParameter.Optional}, _
        New With {.id = "\d+"} _
    )


    ''# Default Catch All Valid Routes
    routes.MapRouteLowercase( _
        "Default", _
        "{controller}/{action}/{id}/{slug}", _
        New With {.controller = "Events", .action = "Index", .id = UrlParameter.Optional, .slug = UrlParameter.Optional} _
    )

    ''# Catch All InValid (NotFound) Routes
    routes.MapRoute( _
        "NotFound", _
        "{*url}", _
        New With {.controller = "Error", .action = "NotFound"})

End Sub

我的“未找到”路线什么也没做。

【问题讨论】:

    标签: asp.net asp.net-mvc-2 routing http-status-code-404 handleerror


    【解决方案1】:

    在网络配置中设置它:

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

    error.html 是您可以向用户显示任意 HTML 的自定义页面。

    【讨论】:

    • 就像我说的,我不想重定向用户。
    • 如果您在路由字典中设置一个路由,该路由有未知路由路由到您的错误控制器/操作结果并返回您想要的视图。这将使 URL 保持完整。
    • 嗯,这样的路线会是什么样子?
    • -1 因为答案是“重定向”,这正是我不想要的。
    【解决方案2】:

    找到我的答案on my other SO question。非常感谢Anh-Kiet Ngo 的解决方案。

    protected void Application_Error(object sender, EventArgs e)
    {
        Exception exception = Server.GetLastError();
    
        // A good location for any error logging, otherwise, do it inside of the error controller.
    
        Response.Clear();
        HttpException httpException = exception as HttpException;
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "YourErrorController");
    
        if (httpException != null)
        {
            if (httpException.GetHttpCode() == 404)
            {
                routeData.Values.Add("action", "YourErrorAction");
    
                // We can pass the exception to the Action as well, something like
                // routeData.Values.Add("error", exception);
    
                // Clear the error, otherwise, we will always get the default error page.
                Server.ClearError();
    
                // Call the controller with the route
                IController errorController = new ApplicationName.Controllers.YourErrorController();
                errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
            }
        }
    }
    

    【讨论】:

    • 这是一个很好的解决方案,但是在 errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));被调用,这一行需要加上Response.StatusCode = 404;如果不添加此行,则无论为用户呈现什么,页面响应仍然是 200。
    • IController.Execute 是受保护的,至少在 MVC 4 中是这样,所以我只需要将它从我的控制器中包装出来: public void Exec(RequestContext requestContext) { this.Execute(requestContext); }
    【解决方案3】:

    你可以试试:

    <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404" redirect="~/Error.aspx"/>
    </customErrors>
    

    http://msdn.microsoft.com/en-us/library/system.web.configuration.customerrorssection.redirectmode.aspx

    如果将 RedirectMode 属性设置为 ResponseRewrite,则将用户发送到错误页面,并且浏览器中的原始 URL 不会更改。

    【讨论】:

    • ResponseRewrite 有时会出现问题,如果错误出现在 WebForms 页面中并且重定向是 MVC 页面,反之亦然。
    • @Keith,你能扩展一下吗?我遇到了这些情况,我想尝试理解为什么它只在某些时候有效......
    • @RaulVejar 不是真的,这是一篇旧帖子(我认为问题出在 MVC2 上),我们只是通过 MVC 和 WebForms 的不同自定义错误来避免它
    • @Keith,不想这么说,但 MVC4 中仍然存在问题。我们最终不得不在全局 asax 上手动处理错误并完全关闭自定义错误
    【解决方案4】:

    这是 ASP.NET MVC 框架的一大失误,因为如果没有像 1 answer 这样的“紧缩”,您就无法自定义(真正的自定义我的意思不仅仅是一个错误页面)404 页面。您可以对所有未找到的原因使用一个 404 错误,或者您可以通过 [HandleError] 自定义多个 500 错误页面,但您不能以声明方式自定义 404 错误。由于 SEO 原因,500 错误是不好的。 404 错误是好的错误。

    【讨论】:

      【解决方案5】:

      路由是模式匹配。您未找到的路由不起作用,因为您的错误 URL 的模式与较早的路由匹配。

      所以:

      ''# Default Catch All Valid Routes
      routes.MapRouteLowercase( _
          "Default", _
          "{controller}/{action}/{id}/{slug}", _
          New With {.controller = "Events", .action = "Index", .id = UrlParameter.Optional, .slug = UrlParameter.Optional} _
      )
      
      ''# Catch All InValid (NotFound) Routes
      routes.MapRoute( _
          "NotFound", _
          "{*url}", _
          New With {.controller = "Error", .action = "NotFound"})
      

      如果您输入:http://example.com/asdf-,那么它匹配上面的“默认”路由 - MVC 去寻找 asdf-Controller 并且找不到它,所以抛出异常。

      如果您转到http://example.com/asdf-/action/id/slug/extra,则“默认”不再匹配,而是将遵循“未找到”路线。

      您可以将所有已知控制器的过滤器添加到“默认”路由,但添加新控制器时维护起来会很麻烦。

      您根本不需要挂钩 Application_Error 事件,但我还没有找到解决丢失控制器的更好方法 - 我以 another question 询问它。

      【讨论】:

      • 这对我帮助很大。非常感谢
      【解决方案6】:

      我只需返回基本控制器中定义的动作,而不是重定向到它。

      [HandleError]
      public ActionResult NotFound()
      {
          Response.StatusCode = 404;
          Response.TrySkipIisCustomErrors = true;     
          return View("PageNotFound", SearchUtilities.GetPageNotFoundModel(HttpContext.Request.RawUrl));          
      }
      

      (“SearchUtilities.GetPageNotFoundModel”位是关于通过将 URL 输入我们的搜索引擎来生成建议)

      在任何继承基础的动作方法中,我都可以简单地调用它:

      return NotFound();
      

      ...每当我捕获到无效参数时。包罗万象的路线也是如此。

      【讨论】:

      • 这没关系,但我现在可以在不需要 NotFound 操作的情况下做到这一点。让事情变得更干净。
      • 我明白了。这很吸引人。您如何处理匹配的路由,因此不要抛出 404,而是传递无效参数?
      • 我有一个自定义的HandleNotFoundException,我可以从一个有效的控制器中抛出它。它将加载适当的共享视图Shared/NotFound.cshtml,但仍保持 URI 不变。 throw HandleNotFoundException
      • 我上面的答案已经过时了......我需要用我的解决方案更新它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-23
      • 2011-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多