ASP.NET MVC - 使用Reflection查找控制器是否存在

时间:2010-08-07 21:24:39

标签: reflection asp.net-mvc-2 routing constraints

我很想知道如何正确实施404重定向。

如果我使用以下

<HandleError()> _
Public Class BaseController : Inherits System.Web.Mvc.Controller
''# do stuff
End Class

然后页面上任何未处理的错误都会加载“错误”视图,该视图效果很好。 http://example.com/user/999(其中999是无效的用户ID)会在保留原始网址时抛出错误(这就是我想要的)

然而。如果有人将http://example.com/asdfjkl输入到url(其中asdfjkl是无效的控制器),则IIS将抛出通用404页面。 (这是我想要的)。我需要的是上面要应用的相同内容。保留原始URL,并加载“NotFound”控制器。

我正在注册我的路线

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")

    routes.MapRoute("Start", "", New With {.controller = "Events", .action = "Index"})

    ''# 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 MapRoute
    routes.MapRouteLowercase("Default", "{controller}/{action}/{id}/{slug}", _
                             New With {.controller = "Events", .action = "Index", .id = UrlParameter.Optional, .slug = UrlParameter.Optional}, _
                             New With {.controller = New ControllerExistsConstraint})

    ''# Catch everything else cuz they're 404 errors
    routes.MapRoute("CatchAll", "{*catchall}", _
                    New With {.Controller = "Error", .Action = "NotFound"})

End Sub

注意ControllerExistsConstraint?我需要做的是使用Reflection来发现控制器是否存在。

有人可以帮助我填补空白吗?

Public Class ControllerExistsConstraint : Implements IRouteConstraint

    Public Sub New()
    End Sub

    Public Function Match(ByVal httpContext As System.Web.HttpContextBase, ByVal route As System.Web.Routing.Route, ByVal parameterName As String, ByVal values As System.Web.Routing.RouteValueDictionary, ByVal routeDirection As System.Web.Routing.RouteDirection) As Boolean Implements System.Web.Routing.IRouteConstraint.Match


        ''# Bah, I can't figure out how to find if the controller exists


End Class

我也想知道这个性能的影响......性能如何重要反思?如果它太多了,有更好的方法吗?

4 个答案:

答案 0 :(得分:10)

我有一个C#解决方案,我希望它有所帮助。我抄袭了一些代码,虽然对于我的生活,我找不到我从哪里得到它。如果有人知道,请告诉我,以便我可以将其添加到我的评论中。

此解决方案不使用反射,但会查看所有应用程序错误(异常)并检查它是否为404错误。如果是,那么它只是将当前请求路由到不同的控制器。虽然我不是任何方面的专家,但我认为这个解决方案可能比反思更快。无论如何,这是解决方案,它进入你的Global.asax.cs,

    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));
            }
        }
    }

所以控制器就是,

public class YourErrorController : Controller
{
    public ActionResult YourErrorAction()
    {
        return View();
    }
}

答案 1 :(得分:2)

这是一个非常类似的问题to mine,但我喜欢你的替代方法。

我认为作为动态过滤器的反射可能过于沉重,但我认为我有更好的方法 - 您可以通过正则表达式过滤允许的操作:

// build up a list of known controllers, so that we don't let users hit ones that don't exist
var allMvcControllers = 
    from t in typeof(Global).Assembly.GetTypes()
    where t != null &&
        t.IsPublic &&
        !t.IsAbstract &&
        t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
        typeof(IController).IsAssignableFrom(t)
    select t.Name.Substring(0, t.Name.Length - 10);

// create a route constraint that requires the controller to be one of the reflected class names
var controllerConstraint = new
{
    controller = "(" + string.Join("|", allMvcControllers.ToArray()) + ")"
};

// default MVC route
routes.MapRoute(
    "MVC",
    "{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional },
    controllerConstraint);

// fall back route for unmatched patterns or invalid controller names
routes.MapRoute(
    "Catch All", 
    "{*url}",
    new { controller = "System", action = "NotFound" });

然后我在我的基础Controller上添加了另一种方法:

protected override void HandleUnknownAction(string actionName)
{
    this.NotFound(actionName).ExecuteResult(this.ControllerContext);
}

在这种情况下,BaseController.NotFound处理有效控制器上的缺失操作。

最后:

  • {site}/invalid - 由基于新反射的过滤器
  • 找到
  • {site}/valid/notAnAction - 由HandleUnknownAction
  • 找到
  • {site}/valid/action/id - 通过代码中的代码检查找到(如前所述)
  • {site}/valid/action/id/extraPath - 通过不匹配任何路线但发现全部
  • 找到

我认为这涵盖了所有404场景: - )

答案 2 :(得分:1)

也许这篇文章可以指出正确的方向:ASP.NET MVC: Get all controllers

答案 3 :(得分:-1)

为什么不在web.config文件中使用自定义错误捕获它们并避免一堆反射?

<customErrors mode="On">   
    <error statusCode="404" redirect="/Error/NotFound" />
</customErrors>