ASP.NET MVC 4路由我只是想不通

时间:2014-01-01 08:28:10

标签: asp.net-mvc asp.net-mvc-4 asp.net-mvc-routing

我正在使用ASP.NET MVC 4中的一个项目,而且我对某个特定的路由有点不知所措。我已经在项目中有很多自定义路线。

我目前正在为网站的前端(公开可见部分)制作一堆控制器,以便能够执行abc.com/OurSeoFeatures之类的路由到/OurSeoFeatures/Index

有没有办法做到这一点,以便上面会路由到/frontend/OurSeoFeature之类的东西,而另一个页面会路由到/frontend/anotherpage并且还有正确的其他路线?在我看来,上面的内容将达到默认路线,如果我放下类似下面的东西,它会抓住所有请求,不会让我碰到其他任何东西。

        routes.MapRoute(
            name: "ImpossibleRoute",
            url: "{action}/{id}",
            defaults: new { controller = "frontend", id = UrlParameter.Optional }
        );

我只是坚持制作一堆控制器?我真的不想制作一个像page这样的控制器,并在那里放了很多动作,因为我觉得它不是很漂亮。任何想法?

3 个答案:

答案 0 :(得分:1)

为了做你要求的事情,你只需要添加路线约束:

routes.MapRoute(
    name: "Frontend",
    url: "frontend/{controller}/{action}/{id}",
    defaults: new { controller = "OurSeoFeature", action = "Index", id = UrlParameter.Optional },
    constraints: new { controller = "OurSeoFeature|Products" }
);

此约束表示路由仅匹配名称为OurSeoFeatureControllerProductsController的控制器。任何其他控制器将触发默认路由。但是,这不会处理将这些控制器重定向到/frontend/...,如果这是您所追求的。相反,这需要更多参与。

首先,您需要创建一个实现IRouteConstraint的类,以便提供您想要重定向到/frontend/...的控制器名称。我们现在需要这个的原因是因为我们需要在ActionFilter中访问这些名称,如果我们提供上面的constraints: new { controller = "OurSeoFeature|Products"之类的正则表达式约束,我们就不能这样做。因此,约束可能看起来像这样:

public class FrontendControllerConstraint : IRouteConstraint
{
    public FrontendControllerConstraint()
    {
        this.ControllerNames = new List<string> { "OurSeoFeature", "Products" }; 
    }

    public bool Match(HttpContextBase httpContext, Route route,
        string parameterName, RouteValueDictionary values,
        RouteDirection routeDirection)
    {
        string value = values[parameterName].ToString();

        return ControllerNames.Contains(value, StringComparer.OrdinalIgnoreCase);
    }

    public List<string> ControllerNames { get; private set; }
}

接下来,动作过滤器可能如下所示:

public class RedirectToFrontendActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var controller = filterContext.RouteData.Values["controller"].ToString();
        var path = filterContext.HttpContext.Request.Url.AbsolutePath;
        var controllersToMatch = new FrontendControllerConstraint().ControllerNames;

        if (controllersToMatch.Contains(controller, StringComparer.OrdinalIgnoreCase)
            && path.IndexOf(pathPrefix, StringComparison.OrdinalIgnoreCase) == -1)
        {
            filterContext.Result =
                new RedirectToRouteResult(routeName, filterContext.RouteData.Values);
        }

        base.OnActionExecuting(filterContext);
    }

    private string routeName = "Frontend";
    private string pathPrefix = "Frontend";
}

现在我们已经有了这些,剩下的就是把它连接起来。首先,约束的应用方式略有不同:

routes.MapRoute(
    name: "Frontend",
    url: "frontend/{controller}/{action}/{id}",
    defaults: new { controller = "OurSeoFeature", action = "Index", id = UrlParameter.Optional },
    constraints: new { controller = new FrontendControllerConstraint() }
);

最后,您需要将过滤器添加到FilterConfig.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new RedirectToFrontendActionFilter());
}

这里有一个警告,因为我正在检查Request.Url.AbsolutePath,您无法在包含单词frontend的路径中传递任何内容。因此,请确保添加到路径中的所有控制器,操作和路由值都不包含该值。原因是我正在检查路径中是否存在/frontend/,以确保匹配的控制器只有在他们尚未使用它时才会重定向到该路由。

您可以使用该设置添加许多内容,但我不了解您的要求。因此,您应该将此代码简单地视为一个开始的骨架,确保测试它是否符合您的要求。

每条评论更新

我会将所有内容都放在那里,以防万一有人觉得有用。但是,要解决您想要做的事情,我们需要采用不同的方法。同样,我们需要一些路由约束,但我看到这个工作的方式是翻转你的想法,并使前端成为默认路由。像这样:

routes.MapRoute(
    name: "Backend",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    constraints: new { controller = "Home|Backend" }
);

routes.MapRoute(
    name: "Default",
    url: "{action}/{id}",
    defaults: new { controller = "Frontend", action = "Index", id = UrlParameter.Optional },
    constraints: new { action = "Index|OurSeoFeature" }
);

和以前一样,我已经应用了一些约束来获得正确的行为。特别是,对于这种约束:

constraints: new { controller = "Home|Backend" }

如果您有许多不属于前端的控制器,那么实现IRouteConstraint以保留控制器名称列表可能是个主意。您甚至可以从基本控制器中获取所有后端控制器,因此您可以在IRouteConstraint实现中使用反射来获取所有后端控制器。像这样:

public BackendController : Controller
{
    // 
}

然后:

public AdminController : BackendController
{
    //
}

约束:

public class BackendConstraint : IRouteConstraint
{
    // Get controller names based on types that
    // BackendController
}

同样的想法也适用于为第二个约束获取FrontendController的动作名称。这里唯一需要注意的是,您没有任何与FrontendController上的操作同名的后端控制器,因为它将匹配错误的路由。

答案 1 :(得分:1)

我很欣赏这个问题已经过了一年多了,并且已经接受了答案,但是接受的答案涉及到没有必要时的路线限制。它真的很简单:

routes.MapRoute("SEO", "OurSeoFeatures",
               new { controller = "frontEnd", action = "OurSeoFeatures"});

答案 2 :(得分:-1)

路线的基本思路是控制器/动作。

因此,如果您想要点击OurSeoFeatures控制器的索引操作,那么您必须提供类似

的路线
routes.MapRoute(
            name: "BasicController",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "OurSeoFeatures",action="Index", id = UrlParameter.Optional }
        );

在您的情况下,您已从路线网址中省略了控制器。请将控制器指定为URL的一部分,并具有默认控制器。