I want to setup routing as follows:
/Profile/Edit -> routes to Edit action
/Profile/Add -> routes to Add action
/Profile/username -> routes to Index action with parameter username, because action username doesn't exist.
So I want the second parameter to be parsed as the controller action, except when no controller with that name exists; then it should route to the default index page and use the url part as id.
Possible?
Matt's solution gets you 90% of the way. However, instead of using a route constraint to exclude action names, use a route constraint to include only valid usernames, like so:
public class MustMatchUserName : IRouteConstraint
{
private Users _db = new UserEntities();
public MustMatchUserName()
{ }
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return _db.Users.FirstOrDefault(x => x.UserName.ToLower() == values[parameterName].ToString().ToLower()) != null;
}
}
Then, as Matt points out, in the user creation process, you must enforce a rule that your ActionNames are not valid for user names.
counsellorben
You can use regex in your route constraints like so
routes.MapRoute(
"UserProfileRoute",
"Profile/{username}",
new { controller = "Profile", action = "Index" },
new { username = "(?i)(?!edit$|add$)(.*)" });
this will match urls like /profile/addendum
/profile/someusername
and will ignore /profile/edit
and /profile/add
Here is one way to accomplish this:
Make these your routes in Global.asax.cs:
routes.MapRoute("UserProfileRoute", "Profile/{username}",
new { controller = "Profile", action = "Index" });
routes.MapRoute("DefaultProfileRoute", "Profile/{action}",
new { controller = "Profile", action = "SomeDefaultAction" });
This will match /Profile/someUsername as expected. But it will fail for all other actions. All action names are assumed to be usernames now. A quick fix to this is to add an IRouteConstraint to the first route:
routes.MapRoute("UserProfileRoute", "Profile/{username}",
new { controller = "Profile", action = "Index" },
new { username = new NotAnActionRouteConstraint() });
routes.MapRoute("DefaultProfileRoute", "Profile/{action}",
new { controller = "Profile", action = "SomeDefaultAction" });
public class NotAnActionRouteConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string value = values[parameterName].ToString();
// it is likely parameterName is not cased correctly,
// something that would need to be
// addressed in a real implementation
return typeof(ProfileController).GetMethod(parameterName,
BindingFlags.Public | BindingFlags.Instance) == null;
}
}
However, this is a bit ugly. Hopefully someone knows of a better solution.
You also have problems when one of your users picks a name that is the same as an action :)
Anything is possible. However, why not just make /profile your root?
If that isn't possible, you may need to hardcode your action's routes.