我用的是RC2

使用URL路由:

routes.MapRoute(
    "Error",
     "{*url}",
     new { controller = "Errors", action = "NotFound" }  // 404s
);

上面似乎照顾到这样的请求(假设默认路由表由最初的MVC项目设置):"/blah/blah/blah/blah"

重写控制器本身的HandleUnknownAction():

// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
    ViewData["actionName"] = actionName;
    View("NotFound").ExecuteResult(this.ControllerContext);
}  

但是,前面的策略不处理对坏/未知控制器的请求。例如,我没有“/IDoNotExist”,如果我请求这个,我从web服务器得到通用404页面,而不是我的404,如果我使用路由+覆盖。

最后,我的问题是:有没有办法在MVC框架中使用路由或其他东西来捕获这种类型的请求?

或者我应该默认使用Web。配置customErrors作为我的404处理程序,忘记这一切?我假设如果我使用customErrors,由于Web的原因,我将不得不在/Views之外存储通用404页面。配置直接访问限制。


当前回答

下面是另一个使用MVC工具的方法,你可以处理对坏控制器名、坏路由名的请求,以及任何其他你认为在Action方法中合适的条件。就我个人而言,我倾向于尽量避免使用网络。配置设置,因为他们做302 / 200重定向,不支持ResponseRewrite (Server.Transfer)使用Razor视图。出于搜索引擎优化的原因,我更喜欢返回带有自定义错误页面的404。

其中一些是对cottsak上面的技术的新采取。

这个解决方案也使用最少的网络。配置设置偏向MVC 3错误过滤器。

使用

只需从动作或自定义ActionFilterAttribute抛出一个HttpException。

Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")

步骤1

将以下设置添加到您的web.config。这是使用MVC的HandleErrorAttribute所必需的。

<customErrors mode="On" redirectMode="ResponseRedirect" />

步骤2

添加一个自定义的HandleHttpErrorAttribute,类似于MVC框架的HandleErrorAttribute,除了HTTP错误:

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"

    Private m_HttpCode As HttpStatusCode
    Private m_Master As String
    Private m_View As String

    Public Property HttpCode As HttpStatusCode
        Get
            If m_HttpCode = 0 Then
                Return HttpStatusCode.NotFound
            End If
            Return m_HttpCode
        End Get
        Set(value As HttpStatusCode)
            m_HttpCode = value
        End Set
    End Property

    Public Property Master As String
        Get
            Return If(m_Master, String.Empty)
        End Get
        Set(value As String)
            m_Master = value
        End Set
    End Property

    Public Property View As String
        Get
            If String.IsNullOrEmpty(m_View) Then
                Return String.Format(m_DefaultViewFormat, Me.HttpCode)
            End If
            Return m_View
        End Get
        Set(value As String)
            m_View = value
        End Set
    End Property

    Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
        If filterContext Is Nothing Then Throw New ArgumentException("filterContext")

        If filterContext.IsChildAction Then
            Return
        End If

        If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
            Return
        End If

        Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
        If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
            Return
        End If

        If ex.GetHttpCode <> Me.HttpCode Then
            Return
        End If

        Dim controllerName As String = filterContext.RouteData.Values("controller")
        Dim actionName As String = filterContext.RouteData.Values("action")
        Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)

        filterContext.Result = New ViewResult With {
            .ViewName = Me.View,
            .MasterName = Me.Master,
            .ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
            .TempData = filterContext.Controller.TempData
        }
        filterContext.ExceptionHandled = True
        filterContext.HttpContext.Response.Clear()
        filterContext.HttpContext.Response.StatusCode = Me.HttpCode
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
    End Sub
End Class

步骤3

在Global.asax中的GlobalFilterCollection (GlobalFilters.Filters)中添加过滤器。这个例子将所有InternalServerError(500)错误路由到Error共享视图(Views/ shared /Error.vbhtml)。NotFound(404)错误将被发送到ErrorHttp404。共享视图中的VBHTML。我在这里添加了一个401错误,以向您展示如何将其扩展为其他HTTP错误代码。注意,这些必须是共享视图,并且它们都使用System.Web.Mvc.HandleErrorInfo对象作为模型。

filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})

步骤4

创建一个基控制器类,并在控制器中继承它。这一步允许我们处理未知的动作名称,并向我们的HandleHttpErrorAttribute抛出HTTP 404错误。

Public Class BaseController
    Inherits System.Web.Mvc.Controller

    Protected Overrides Sub HandleUnknownAction(actionName As String)
        Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
    End Sub

    Public Function Unknown() As ActionResult
        Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
        Return New EmptyResult
    End Function
End Class

步骤5

创建一个ControllerFactory重写,并在全局中重写它。asax文件在Application_Start。这个步骤允许我们在指定了无效的控制器名时引发HTTP 404异常。

Public Class MyControllerFactory
    Inherits DefaultControllerFactory

    Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
        Try
            Return MyBase.GetControllerInstance(requestContext, controllerType)
        Catch ex As HttpException
            Return DependencyResolver.Current.GetService(Of BaseController)()
        End Try
    End Function
End Class

'In Global.asax.vb Application_Start:

controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)

步骤6

在你的路由表中包含一个特殊的路由。BaseController Unknown动作的路由。这将帮助我们在用户访问未知控制器或未知操作的情况下引发404。

'BaseController
routes.MapRoute( _
    "Unknown", "BaseController/{action}/{id}", _
    New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)

总结

这个例子演示了如何使用MVC框架向浏览器返回404 Http错误代码,而不需要使用过滤器属性和共享错误视图进行重定向。它还演示了当指定无效的控制器名和动作名时显示相同的自定义错误页面。

我将添加一个无效的控制器名,动作名的截图,以及一个自定义404从Home/TriggerNotFound动作引发,如果我得到足够的投票,张贴一个=)。提琴手返回404消息时,我访问以下url使用这个解决方案:

/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound

Cottsak上面的文章和这些文章都是很好的参考。

使用CustomErrors redirectMode=ResponseRewrite的问题 Elmah + MVC HandleErrorAttribute

其他回答

我对未处理区域、控制器和动作的简化解决方案:

Create a view 404.cshtml. Create a base class for your controllers: public class Controller : System.Web.Mvc.Controller { protected override void HandleUnknownAction(string actionName) { Http404().ExecuteResult(ControllerContext); } protected virtual ViewResult Http404() { Response.StatusCode = (int)HttpStatusCode.NotFound; return View("404"); } } Create a custom controller factory returning your base controller as a fallback: public class ControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType != null) return base.GetControllerInstance(requestContext, controllerType); return new Controller(); } } Add to Application_Start() the following line: ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));

因为我的评论太长了,所以发表了一个答案…

这既是对独角兽帖子/答案的评论,也是对它的问题:

https://stackoverflow.com/a/7499406/687549

比起其他答案,我更喜欢这个答案,因为它很简单,而且事实是,显然微软的一些人被咨询了。然而,我有三个问题,如果他们可以回答,那么我将把这个答案称为互联网上所有404/500个错误答案的圣杯。NET MVC (x)应用程序。

@Pure。Krome

Can you update your answer with the SEO stuff from the comments pointed out by GWB (there was never any mentioning of this in your answer) - <customErrors mode="On" redirectMode="ResponseRewrite"> and <httpErrors errorMode="Custom" existingResponse="Replace">? Can you ask your ASP.NET team friends if it is okay to do it like that - would be nice to have some confirmation - maybe it's a big no-no to change redirectMode and existingResponse in this way to be able to play nicely with SEO?! Can you add some clarification surrounding all that stuff (customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", REMOVE customErrors COMPLETELY as someone suggested) after talking to your friends at Microsoft?

就像我说的;如果我们能让你的回答更完整就太好了,因为这似乎是一个相当受欢迎的问题,有54000 +的浏览量。

更新:Unicorn的答案是302 Found和200 OK,不能使用路由更改为只返回404。它必须是一个物理文件,不是很MVC:ish。我们来看另一个解。太糟糕了,因为这似乎是到目前为止的终极MVC:ish答案。

我真的很喜欢cottsaks的解决方案,认为它解释得非常清楚。我唯一增加的是修改步骤2如下

public abstract class MyController : Controller
{

    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        //if controller is ErrorController dont 'nest' exceptions
        if(this.GetType() != typeof(ErrorController))
        this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

基本上,这将阻止包含无效动作和控制器的url两次触发异常例程。Eg的url,如asdfsdf/dfgdfgd

下面是另一个使用MVC工具的方法,你可以处理对坏控制器名、坏路由名的请求,以及任何其他你认为在Action方法中合适的条件。就我个人而言,我倾向于尽量避免使用网络。配置设置,因为他们做302 / 200重定向,不支持ResponseRewrite (Server.Transfer)使用Razor视图。出于搜索引擎优化的原因,我更喜欢返回带有自定义错误页面的404。

其中一些是对cottsak上面的技术的新采取。

这个解决方案也使用最少的网络。配置设置偏向MVC 3错误过滤器。

使用

只需从动作或自定义ActionFilterAttribute抛出一个HttpException。

Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")

步骤1

将以下设置添加到您的web.config。这是使用MVC的HandleErrorAttribute所必需的。

<customErrors mode="On" redirectMode="ResponseRedirect" />

步骤2

添加一个自定义的HandleHttpErrorAttribute,类似于MVC框架的HandleErrorAttribute,除了HTTP错误:

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"

    Private m_HttpCode As HttpStatusCode
    Private m_Master As String
    Private m_View As String

    Public Property HttpCode As HttpStatusCode
        Get
            If m_HttpCode = 0 Then
                Return HttpStatusCode.NotFound
            End If
            Return m_HttpCode
        End Get
        Set(value As HttpStatusCode)
            m_HttpCode = value
        End Set
    End Property

    Public Property Master As String
        Get
            Return If(m_Master, String.Empty)
        End Get
        Set(value As String)
            m_Master = value
        End Set
    End Property

    Public Property View As String
        Get
            If String.IsNullOrEmpty(m_View) Then
                Return String.Format(m_DefaultViewFormat, Me.HttpCode)
            End If
            Return m_View
        End Get
        Set(value As String)
            m_View = value
        End Set
    End Property

    Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
        If filterContext Is Nothing Then Throw New ArgumentException("filterContext")

        If filterContext.IsChildAction Then
            Return
        End If

        If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
            Return
        End If

        Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
        If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
            Return
        End If

        If ex.GetHttpCode <> Me.HttpCode Then
            Return
        End If

        Dim controllerName As String = filterContext.RouteData.Values("controller")
        Dim actionName As String = filterContext.RouteData.Values("action")
        Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)

        filterContext.Result = New ViewResult With {
            .ViewName = Me.View,
            .MasterName = Me.Master,
            .ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
            .TempData = filterContext.Controller.TempData
        }
        filterContext.ExceptionHandled = True
        filterContext.HttpContext.Response.Clear()
        filterContext.HttpContext.Response.StatusCode = Me.HttpCode
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
    End Sub
End Class

步骤3

在Global.asax中的GlobalFilterCollection (GlobalFilters.Filters)中添加过滤器。这个例子将所有InternalServerError(500)错误路由到Error共享视图(Views/ shared /Error.vbhtml)。NotFound(404)错误将被发送到ErrorHttp404。共享视图中的VBHTML。我在这里添加了一个401错误,以向您展示如何将其扩展为其他HTTP错误代码。注意,这些必须是共享视图,并且它们都使用System.Web.Mvc.HandleErrorInfo对象作为模型。

filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})

步骤4

创建一个基控制器类,并在控制器中继承它。这一步允许我们处理未知的动作名称,并向我们的HandleHttpErrorAttribute抛出HTTP 404错误。

Public Class BaseController
    Inherits System.Web.Mvc.Controller

    Protected Overrides Sub HandleUnknownAction(actionName As String)
        Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
    End Sub

    Public Function Unknown() As ActionResult
        Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
        Return New EmptyResult
    End Function
End Class

步骤5

创建一个ControllerFactory重写,并在全局中重写它。asax文件在Application_Start。这个步骤允许我们在指定了无效的控制器名时引发HTTP 404异常。

Public Class MyControllerFactory
    Inherits DefaultControllerFactory

    Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
        Try
            Return MyBase.GetControllerInstance(requestContext, controllerType)
        Catch ex As HttpException
            Return DependencyResolver.Current.GetService(Of BaseController)()
        End Try
    End Function
End Class

'In Global.asax.vb Application_Start:

controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)

步骤6

在你的路由表中包含一个特殊的路由。BaseController Unknown动作的路由。这将帮助我们在用户访问未知控制器或未知操作的情况下引发404。

'BaseController
routes.MapRoute( _
    "Unknown", "BaseController/{action}/{id}", _
    New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)

总结

这个例子演示了如何使用MVC框架向浏览器返回404 Http错误代码,而不需要使用过滤器属性和共享错误视图进行重定向。它还演示了当指定无效的控制器名和动作名时显示相同的自定义错误页面。

我将添加一个无效的控制器名,动作名的截图,以及一个自定义404从Home/TriggerNotFound动作引发,如果我得到足够的投票,张贴一个=)。提琴手返回404消息时,我访问以下url使用这个解决方案:

/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound

Cottsak上面的文章和这些文章都是很好的参考。

使用CustomErrors redirectMode=ResponseRewrite的问题 Elmah + MVC HandleErrorAttribute

快速回答/ TL

对于那些懒惰的人:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

然后从global.asax中删除这一行

GlobalFilters.Filters.Add(new HandleErrorAttribute());

这仅适用于IIS7+和IIS Express。

如果你用卡西尼号…嗯. .嗯. .呃. .尴尬……


冗长的解释

我知道这个问题已经得到了回答。但答案真的很简单(为大卫·福勒和达米安·爱德华兹真正回答了这个问题而欢呼)。

没有必要做任何定制。

ASP。NET MVC3,所有的片段都在那里。

更新你的网页。在两个点配置。

<system.web>
    <customErrors mode="On" defaultRedirect="/ServerError">
      <error statusCode="404" redirect="/NotFound" />
    </customErrors>

and

<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/NotFound" responseMode="ExecuteURL" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="500" path="/ServerError" responseMode="ExecuteURL" />
    </httpErrors>    

...
<system.webServer>
...
</system.web>

现在仔细记下我决定使用的路线。你可以用任何方法,但我的路线

/NotFound <-表示404未找到,错误页面。 /ServerError <-对于任何其他错误,包括在我的代码中发生的错误。这是一个500内部服务器错误

请参阅<system. >中的第一部分。Web >只有一个自定义条目?statusCode="404"条目?我只列出了一个状态码,因为所有其他错误,包括500服务器错误(即。当你的代码有bug并使用户的请求崩溃时发生的那些讨厌的错误)。所有其他错误都通过设置defaultRedirect="/ServerError"来处理。上面说,如果你没有404页面没有找到,那么请转到route /ServerError。

好的。那太离谱了。现在到global.asax中列出的路由

步骤2 -在Global.asax中创建路由

这是我的完整路线部分。

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("{*favicon}", new {favicon = @"(.*/)?favicon.ico(/.*)?"});

    routes.MapRoute(
        "Error - 404",
        "NotFound",
        new { controller = "Error", action = "NotFound" }
        );

    routes.MapRoute(
        "Error - 500",
        "ServerError",
        new { controller = "Error", action = "ServerError"}
        );

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
        );
}

它列出了两个忽略路由-> axd's和favicons(哦!奖励忽略路线,为你!) 然后(这里的顺序是IMPERATIVE),我有两个显式的错误处理路由..然后是其他路线。在本例中,是默认值。当然,我还有更多,但那是我网站的特色。只要确保错误路由位于列表的顶部即可。秩序是必须的。

最后,当我们在全局变量中。asax文件,我们不全局注册HandleError属性。不,不,不,先生。没有。年兽。负的。Noooooooooo……

从global.asax中删除这一行

GlobalFilters.Filters.Add(new HandleErrorAttribute());

步骤3 -用动作方法创建控制器

现在. .我们添加了一个带有两个动作方法的控制器…

public class ErrorController : Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ActionResult ServerError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        // Todo: Pass the exception into the view model, which you can make.
        //       That's an exercise, dear reader, for -you-.
        //       In case u want to pass it to the view, if you're admin, etc.
        // if (User.IsAdmin) // <-- I just made that up :) U get the idea...
        // {
        //     var exception = Server.GetLastError();
        //     // etc..
        // }

        return View();
    }

    // Shhh .. secret test method .. ooOOooOooOOOooohhhhhhhh
    public ActionResult ThrowError()
    {
        throw new NotImplementedException("Pew ^ Pew");
    }
}

好的,我们来看看这个。首先,这里没有[HandleError]属性。为什么?因为内置的ASP。NET框架已经在处理错误,并且我们已经指定了处理错误所需的所有屁事:)它就在这个方法中!

接下来,我有两个动作方法。没什么难的。如果你想显示任何异常信息,那么你可以使用Server.GetLastError()来获取该信息。

奖励WTF:是的,我做了第三个动作方法,以测试错误处理。

步骤4 -创建视图

最后,创建两个视图。把它们放到这个控制器的正常视图点。

奖金的评论

你不需要一个Application_Error(对象发送器,EventArgs e) 以上步骤对Elmah来说都是100%完美的。该死的埃尔玛!

朋友们,就这样吧。

现在,祝贺你读了这么多,并有一个独角兽作为奖品!