ASP.NET MVC - 如何抛出类似于StackOverflow上的404页面

时间:2010-08-07 16:39:56

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

我目前有一个继承自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(IE:不重定向到任何其他页面),但只需显示“NotFound”视图,并将HttpStatusCode.NotFound推送到客户端。

例如,只需访问https://stackoverflow.com/asdf,您就会在其中看到自定义404页面,并看到网页上留下的网址。

显然我错过了什么,但我无法弄明白。因为“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

我的“NotFound”路线什么也没做。

6 个答案:

答案 0 :(得分:9)

找到我的答案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));
        }
    }
}

答案 1 :(得分: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。

答案 2 :(得分:1)

路线是模式匹配。您找不到路线不起作用,因为错误网址的模式与之前的路线匹配。

所以:

''# 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,则“默认”不再匹配,而是会关注“NotFound”路线。

您可以为所有已知控制器添加一个过滤器到“默认”路由,但是当您添加新控制器时,这将很难维护。

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

答案 3 :(得分:1)

我只需返回基本控制器中定义的操作,而不是重定向,就可以使用它。

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

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

在任何继承基础的行动方法中,我可以简单地称之为:

return NotFound();

...每当我发现无效参数时。而且全能路线也是如此。

答案 4 :(得分:0)

在网络配置中进行设置:

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

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

答案 5 :(得分:0)

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