ASP.NET routing - avoiding clashes between controller/action and vanity/slug urls
I'm looking for a good solution to having a URL scheme that works for both standard ASP.NET MVC controller/action urls eg:
/Home/About -开发者_如何学运维-> Controller "Home", Action "About"
and vanity/slug urls eg:
/fred/post --> Controller "Posts", Action "View", User "fred", Post "post"
Importantly, I want the outbound url generation to work so that
Html.ActionLink("View", "Posts", new { User="fred", Post="post" }, null }
gives /fred/post - not /Posts/View/fred/post
It seems, I can get it to work for either inbound or outbound routing but not both. Or I can get it sort of working but it's messy and prone to breaking. What approaches, tips and tricks are there to getting something like this working cleanly?
I finally came up with the solution of using a routing constraint that can check if a parameter matches (or doesn't match) the name of a controller:
public class ControllerConstraint : IRouteConstraint
{
static List<string> ControllerNames = (from t in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
where typeof(IController).IsAssignableFrom(t) && t.Name.EndsWith("Controller")
select t.Name.Substring(0, t.Name.Length - 10).ToLower()).ToList();
bool m_bIsController;
public ControllerConstraint(bool IsController)
{
m_bIsController = IsController;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (m_bIsController)
return ControllerNames.Contains(values[parameterName].ToString().ToLower());
else
return !ControllerNames.Contains(values[parameterName].ToString().ToLower());
}
}
Use like this:
// eg: /myusername
routes.MapRoute(
"MemberUrl",
"{member_urlid}",
new { controller = "Members", action = "View" },
new { action="View", member_urlid = new ControllerConstraint(false) }
);
// eg: /myusername/mypagename
routes.MapRoute(
"ItemUrl",
"{member_urlid}/{item_urlid}",
new { controller = "Items", action = "View" },
new { action="View", member_urlid = new ControllerConstraint(false) }
);
// Normal controller/action routes follow
The constraint new ControllerConstraint(false)
means don't match this routing rule if the parameter matches the name of a controller. Pass true to make the constraint check that the parameter does match a controller name.
精彩评论