我正在使用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
这样的控制器,并在那里放了很多动作,因为我觉得它不是很漂亮。任何想法?
答案 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" }
);
此约束表示路由仅匹配名称为OurSeoFeatureController
或ProductsController
的控制器。任何其他控制器将触发默认路由。但是,这不会处理将这些控制器重定向到/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的一部分,并具有默认控制器。