下面是另一个使用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