MVC routing question

2019-05-22 05:05发布

问题:

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?

回答1:

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



回答2:

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



回答3:

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



回答4:

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.