这里有另一种使用MVC工具的方法,您可以在Action方法内处理对于错误控制器名称、错误路由名称和任何其他标准的请求。个人而言,我更喜欢尽可能避免使用web.config设置,因为它们会执行302/200重定向并不支持使用Razor视图的ResponseRewrite (
Server.Transfer
)。基于SEO原因,我更愿意返回一个自定义错误页面的404错误。
其中一些是对以上cottsak技术的新尝试。
此解决方案还使用了最少量的web.config设置,更倾向于使用MVC 3错误过滤器。
用法:
只需从动作或自定义ActionFilterAttribute抛出HttpException即可。
Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")
步骤1
将以下设置添加到您的web.config文件中。这是使用MVC的HandleErrorAttribute所必需的。
<customErrors mode="On" redirectMode="ResponseRedirect" />
步骤2
添加一个类似于MVC框架的HandleErrorAttribute的自定义HandleHttpErrorAttribute,但是用于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
步骤三
在Global.asax
中的全局过滤器集合(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"})
步骤四
创建一个基础控制器类,并在您的控制器中继承它。这一步允许我们处理未知的操作名称并将HTTP 404错误提供给我们的HandleHttpErrorAttribute。
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
步骤五
创建一个ControllerFactory重写,在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
在你的RoutTable.Routes中包含一个特殊的路由,用于BaseController Unknown action。这将帮助我们在用户访问未知控制器或未知操作时引发404错误。
'BaseController
routes.MapRoute( _
"Unknown", "BaseController/{action}/{id}", _
New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)
概要
本示例演示了如何使用MVC框架,通过使用过滤属性和共享错误视图,在不进行重定向的情况下向浏览器返回404 Http错误代码。它还演示了在指定无效的控制器名称和操作名称时显示相同的自定义错误页面。
如果我得到足够的投票来发布截图,我将添加一个无效的控制器名称、操作名称和从Home/TriggerNotFound操作引发的自定义404。使用此解决方案访问以下URL时,Fiddler会返回404消息:
/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound
cottsak上面的帖子以及这些文章都是很好的参考。