开发者

ASP.NET MVC catch-all routing

开发者 https://www.devze.com 2023-03-22 22:43 出处:网络
I have read a few threads on StackOverflow about this, but canno开发者_开发技巧t get it to work. I have this at the end of my RegisterRoutes in Global.asax.

I have read a few threads on StackOverflow about this, but canno开发者_开发技巧t get it to work. I have this at the end of my RegisterRoutes in Global.asax.

routes.MapRoute(
            "Profile",
            "{*url}",
            new { controller = "Profile", action = "Index" }
            );

Basically what I'm trying to achieve is to have mydomain.com/Username point to my member profilepage. How would I have to set up my controller and RegisterRoutes in order for this to work?

Currently mydomain.com/somethingthatisnotacontrollername gets a 404-error.


Solution that works for your case but is not recommended

You have a predefined set of controllers (usually less than 10) in your application so you can put a constraint on controller name and then route everything else to user profile:

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { controller = "Home|Admin|Reports|..." }
);
routes.MapRoute(
    "Profile",
    "{username}/{action}",
    new { controller = "Profile", action = "Details" }
);

But this will not work in case some username is the same as your controller name. It's a small possibility based on experience end empirical data, but it's not 0% chance. When username is the same as some controller it automatically means it will get handled by the first route because constraints won't fail it.

Recommended solution

The best way would be to rather have URL requests as:

www.mydomain.com/profile/username

Why do I recommend it to be this way? Becasue this will make it much simpler and cleaner and will allow to have several different profile pages:

  • details www.mydomain.com/profile/username
  • settings www.mydomain.com/profile/username/settings
  • messages www.mydomain.com/profile/username/messages
  • etc.

Route definition in this case would be like this:

routes.MapRoute(
    "Profile",
    "Profile/{username}/{action}",
    new { controller = "Profile", action = "Details" }
);
routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);


having something that matches to mydomain.com/Username isn't really going to work because there is no way for the routing engine to distinguish between

mydomain.com/someusername

and

mydomain.com/controllername

What might be possible is, if your username scheme has a unique set of properties, i.e. is a sequence of 9 digits, you could define a route to check for something that looks like a username.

routes.MapRoute("",
        "UserRoute",
        "{username}",
        new { controller = "Profile", action = "Index"},
         new { {"username", @"\d{9}"}}
       );

The key point tho, is that you need to provide some way for the routing engine to differentiate between a username and a standard controller action

You can find out more about constraints here


I had a requirement like this for my project. What i did was creating a route constraint like below:

public class SeoRouteConstraint : IRouteConstraint
{
    public static HybridDictionary CacheRegex = new HybridDictionary();
    private readonly string _matchPattern = String.Empty;
    private readonly string _mustNotMatchPattern;

    public SeoRouteConstraint(string matchPattern, string mustNotMatchPattern)
    {
        if (!string.IsNullOrEmpty(matchPattern))
        {
            _matchPattern = matchPattern.ToLower();
            if (!CacheRegex.Contains(_matchPattern))
            {
                CacheRegex.Add(_matchPattern, new Regex(_matchPattern));
            }
        }

        if (!string.IsNullOrEmpty(mustNotMatchPattern))
        {
            _mustNotMatchPattern = mustNotMatchPattern.ToLower();
            if (!CacheRegex.Contains(_mustNotMatchPattern))
            {
                CacheRegex.Add(_mustNotMatchPattern, new Regex(_mustNotMatchPattern));
            }
        }
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        var matchReg = string.IsNullOrEmpty(_matchPattern) ? null : (Regex)CacheRegex[_matchPattern];
        var notMatchReg = string.IsNullOrEmpty(_mustNotMatchPattern) ? null : (Regex)CacheRegex[_mustNotMatchPattern];

        var paramValue = values[parameterName].ToString().ToLower();

        return IsMatch(matchReg, paramValue) && !IsMatch(notMatchReg, paramValue);
    }

    private static bool IsMatch(Regex reg, string str)
    {
        return reg == null || reg.IsMatch(str);
    }
}

Then in the register route method:

routes.MapRoute("",
    "UserRoute",
    "{username}",
    new { controller = "Profile", action = "Index"},
     new { username = new SeoRouteConstraint(@"\d{9}", GetAllControllersName())}
   );

The method GetAllControllersName will return all the controller name in your project separated by | :

private static string _controllerNames;
private static string GetAllControllersName()
{
    if (string.IsNullOrEmpty(_controllerNames))
    {
        var controllerNames = Assembly.GetAssembly(typeof(BaseController)).GetTypes().Where(x => typeof(Controller).IsAssignableFrom(x)).Select(x => x.Name.Replace("Controller", ""));

        _controllerNames = string.Join("|", controllerNames);
    }
    return _controllerNames;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消