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

发布于 2024-09-13 20:21:30 字数 3010 浏览 4 评论 0 原文

我目前有一个继承自 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 的内容(其中 userID 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”路线什么也没做。

I've currently got a BaseController class that inherits from System.Web.Mvc.Controller. On that class I have the HandleError Attribute that redirects users to the "500 - Oops, we screwed up" page. This is currently working as expected.

THIS WORKS

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

''# do stuff
End Class

I also have my 404 pages working on a Per-ActionResult basis which is again working as expected.

THIS WORKS

    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

Again, this works great. If a user enters something like http://example.com/user/999 (where userID 999 doesn't exist), they will see the appropriate 404 page, and yet the URL will not change (they are not redirected to an error page).

I CAN'T GET THIS IDEA TO WORK

Here's where I'm having an issue. If a user enters http://example.com/asdf- they get kicked over to the generic 404 page. What I want to do is leave the URL in tact (IE: not redirect to any other page), but simply display the "NotFound" view as well as push the HttpStatusCode.NotFound to the client.

For an example, just visit https://stackoverflow.com/asdf where you'll see the custom 404 page and see the URL left in tact.

Obviously I'm missing something, but I can't figure it out. Since "asdf" doesn't actually point to any controller, my base controller class isn't kicking in, so I can't do it in the "HandleError" filter in there.

Thanks in advance for the help.

Note: I absolutely do not want to redirect the user to a 404 page. I want them to stay at the existing URL, and I want MVC to push the 404 VIEW to the user.

Edit:

I have also tried the following to no avail.

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

My "NotFound" route is doing nothing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

剩一世无双 2024-09-20 20:21:30

在我的另一个上找到了我的答案所以问题。非常感谢 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));
        }
    }
}

Found my answer on my other SO question. Thanks very much Anh-Kiet Ngo for the solution.

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));
        }
    }
}
居里长安 2024-09-20 20:21:30

您可以尝试:

<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 不会更改。

You could try :

<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

If the RedirectMode property is set to ResponseRewrite, the user is sent to error page and the original URL in the browser is not changed.

菊凝晚露 2024-09-20 20:21:30

路由是模式匹配。您未找到的路由不起作用,因为错误 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 那么“Default”不再匹配,并且将遵循“NotFound”路线。

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

您根本不需要挂钩 Application_Error 事件,但我还没有找到解决丢失控制器的更好方法 - 我将其询问为 另一个问题

Routes are pattern matches. Your not found route isn't working because the pattern of your incorrect URL matches an earlier route.

So:

''# 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"})

If you enter: http://example.com/asdf- then that matches the "Default" route above - MVC goes looking for asdf-Controller and can't find it, so exception thrown.

If you go to http://example.com/asdf-/action/id/slug/extra then "Default" no longer matches and the "NotFound" route will be followed instead.

You could add a filter for all your known controllers to the "Default" route, but that's going to be a pain to maintain when you add new controllers.

You shouldn't need to hook into the Application_Error event at all, but I haven't found a better way around the missing controller yet - I asked it as another question.

月光色 2024-09-20 20:21:30

我通过简单地返回基本控制器中定义的操作来实现此功能,而不是重定向到它。

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

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

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

return NotFound();

... 每当我捕获无效参数时。包罗万象的路线也有同样的作用。

I got this to work by simply returning the action defined in the base controller, rather than redirecting to it.

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

(The "SearchUtilities.GetPageNotFoundModel" bit is about generating suggestions by feeding the URL into our search engine)

In any action method that inherits the base, I can simply call this:

return NotFound();

... whenever I catch an invalid parameter. And the catch-all route does the same.

晚雾 2024-09-20 20:21:30

在 Web 配置中进行设置:

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

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

Set it up in the web config:

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

error.html is your custom page that you can have to display any arbitrary HTML to the user.

夜雨飘雪 2024-09-20 20:21:30

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

It's big miss from ASP.NET MVC framework because you can't customize (real customization I mean not just-one-error-page-for-anything) 404 pages without "crunches" like 1 answer. You can use one 404 error for all not-found causes, or you can customize multiple 500 error pages via [HandleError], but you CAN NOT customize 404 errs declaratively. 500 errs are bad due to SEO reasons. 404 errs are good errs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文