Created
January 27, 2012 08:17
-
-
Save loosechainsaw/1687757 to your computer and use it in GitHub Desktop.
Route Generator for MVC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RouteBuilder.CreateRoutesFrom() | |
.CurrentAssembly() | |
.WithActionNamesWithUnderscoresAsSeperatedRoutes() | |
.IgnoreAspNetMvcDefaultRoute("Home","Index") | |
.AssignRoutes(routes); | |
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] | |
public class ExplicitRouteAttribute : Attribute | |
{ | |
public string Url { get; set; } | |
public RouteValueDictionary Constraints { get; set; } | |
public RouteValueDictionary Defaults { get; set; } | |
public ExplicitRouteAttribute(string url) : this(url,null) | |
{ | |
} | |
public ExplicitRouteAttribute(string url, RouteValueDictionary defaults) : this(url,defaults,null) | |
{ | |
} | |
public ExplicitRouteAttribute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints) | |
{ | |
Url = url; | |
Defaults = defaults; | |
Constraints = constraints; | |
} | |
} | |
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] | |
public class IgnoreActionAttribute : Attribute | |
{ | |
} | |
public class RouteGenerationSemanticModel | |
{ | |
private readonly IList<Assembly> _assembliesToScan; | |
public RouteGenerationSemanticModel() | |
{ | |
Area = String.Empty; | |
Controller = String.Empty; | |
Action = String.Empty; | |
_assembliesToScan = new List<Assembly>(); | |
} | |
public string Area { get; set; } | |
public string Controller { get; set; } | |
public string Action { get; set; } | |
public IRouteGenerationConvention RouteCreationConvention { get; set; } | |
public IEnumerable<Assembly> AssembliesToScan { get { return _assembliesToScan; } } | |
public bool IgnoreAspNetMvcDefaultRoute { get; set; } | |
public void ScanAssembly(Assembly assembly) | |
{ | |
_assembliesToScan.Add(assembly); | |
} | |
} | |
public class RouteGenerationOptions | |
{ | |
public string AreaName { get; set; } | |
public bool IgnoreMvcDefaultRoute { get; set; } | |
public string Controller { get; set; } | |
public string Action { get; set; } | |
} | |
public interface ICanAssignRoutes | |
{ | |
ICanAssignRoutes IgnoreAspNetMvcDefaultRoute(string controller, string action); | |
void AssignRoutes(RouteCollection routes); | |
} | |
public interface ICanBuildRoutesBuilder | |
{ | |
ICanBuildRoutesBuilder CurrentAssembly(); | |
ICanBuildRoutesBuilder AreaName(string area); | |
ICanBuildRoutesBuilder ScanAssembly(string assemblyName); | |
ICanBuildRoutesBuilder ScanAssembly(Assembly assembly); | |
ICanAssignRoutes WithActionNamesAsRoutes(); | |
ICanAssignRoutes WithActionNamesWithUnderscoresAsSeperatedRoutes(); | |
ICanAssignRoutes UsingAspNetMvcDefaults(); | |
ICanAssignRoutes UsingRouteGenerationConvention(IRouteGenerationConvention convention); | |
} | |
public interface IRouteGenerationConvention | |
{ | |
IEnumerable<RouteBase> GetRoutes(IEnumerable<Type> candidates, RouteGenerationOptions options); | |
} | |
public class ActionNameRouteConvention : IRouteGenerationConvention | |
{ | |
public IEnumerable<RouteBase> GetRoutes(IEnumerable<Type> candidates, RouteGenerationOptions options) | |
{ | |
foreach (var type in candidates.Where(x => x.Name.EndsWith("Controller"))) | |
{ | |
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) | |
{ | |
var attrs = method.GetCustomAttributes(false); | |
if(attrs != null && attrs.Any(x => x.GetType() == typeof(IgnoreActionAttribute))) | |
continue; | |
if (IsAspNetDefaultRoute(options, method, type)) | |
continue; | |
var url = String.Format("{0}/", method.Name); | |
var optional = new List<string>(); | |
url = ProcessParameters(optional, url, method); | |
var defaults = new RouteValueDictionary { { "controller", type.Name.Replace("Controller", String.Empty) }, { "action", method.Name } }; | |
foreach (var opt in optional) | |
{ | |
defaults.Add(opt, UrlParameter.Optional); | |
} | |
yield return new Route(url, new MvcRouteHandler()) | |
{ | |
Defaults = defaults | |
}; | |
} | |
} | |
yield break; | |
} | |
private static string ProcessParameters(ICollection<string> optional, string url, MethodInfo method) | |
{ | |
foreach (var parameter in method.GetParameters()) | |
{ | |
if (parameter.IsOptional) | |
optional.Add(parameter.Name); | |
url += "{" + parameter.Name + "}"; | |
} | |
return url; | |
} | |
private static bool IsAspNetDefaultRoute(RouteGenerationOptions options, MethodInfo method, Type type) | |
{ | |
return options.IgnoreMvcDefaultRoute && type.Name == String.Format("{0}Controller", options.Controller) && method.Name == options.Action; | |
} | |
} | |
public class ActionNameWithUnderscoresRouteConvention : IRouteGenerationConvention | |
{ | |
public IEnumerable<RouteBase> GetRoutes(IEnumerable<Type> candidates, RouteGenerationOptions options) | |
{ | |
foreach (var type in candidates.Where(x => x.Name.EndsWith("Controller"))) | |
{ | |
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) | |
{ | |
var attrs = method.GetCustomAttributes(false); | |
if (attrs != null && attrs.Any(x => x.GetType() == typeof(IgnoreActionAttribute))) | |
continue; | |
if (IsAspNetDefaultRoute(options, method, type)) | |
continue; | |
var url = String.Empty; | |
var optional = new List<string>(); | |
if (attrs != null && attrs.Any(x => x.GetType() == typeof(ExplicitRouteAttribute))) | |
continue; | |
url = ProcessUrlSegments(url, method); | |
url = ProcessParameters(optional, url, method); | |
var defaults = new RouteValueDictionary { { "controller", type.Name.Replace("Controller", String.Empty) }, { "action", method.Name } }; | |
foreach (var opt in optional) | |
{ | |
defaults.Add(opt, UrlParameter.Optional); | |
} | |
yield return new Route(url, new MvcRouteHandler()) | |
{ | |
Defaults = defaults | |
}; | |
} | |
} | |
yield break; | |
} | |
private static string ProcessUrlSegments(string url, MethodInfo method) | |
{ | |
if (method.Name.Contains("_")) | |
{ | |
var names = method.Name.Split(new[] { '_' }); | |
url = names.Aggregate(url, (current, name) => current + (name + "/")); | |
} | |
else | |
{ | |
url = String.Format("{0}/", method.Name); | |
} | |
return url; | |
} | |
private static string ProcessParameters(ICollection<string> optional, string url, MethodInfo method) | |
{ | |
foreach (var parameter in method.GetParameters()) | |
{ | |
if (parameter.IsOptional) | |
optional.Add(parameter.Name); | |
url += "{" + parameter.Name + "}"; | |
} | |
return url; | |
} | |
private static bool IsAspNetDefaultRoute(RouteGenerationOptions options, MethodInfo method, Type type) | |
{ | |
return options.IgnoreMvcDefaultRoute && type.Name == String.Format("{0}Controller", options.Controller) && method.Name == options.Action; | |
} | |
} | |
public class AspNetMvcRouteConvention : IRouteGenerationConvention | |
{ | |
public IEnumerable<RouteBase> GetRoutes(IEnumerable<Type> candidates, RouteGenerationOptions options) | |
{ | |
if (options.IgnoreMvcDefaultRoute) | |
yield break; | |
yield return new Route("{controller}/{action}/{id}", new MvcRouteHandler()) | |
{ | |
Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = UrlParameter.Optional }) | |
}; | |
} | |
} | |
public class RouteBuilder : ICanBuildRoutesBuilder, ICanAssignRoutes | |
{ | |
private readonly RouteGenerationSemanticModel model; | |
private RouteBuilder() | |
{ | |
model = new RouteGenerationSemanticModel(); | |
} | |
public static ICanBuildRoutesBuilder CreateRoutesFrom() | |
{ | |
return new RouteBuilder(); | |
} | |
public ICanBuildRoutesBuilder CurrentAssembly() | |
{ | |
model.ScanAssembly(Assembly.GetCallingAssembly()); | |
return this; | |
} | |
public ICanBuildRoutesBuilder AreaName(string area) | |
{ | |
model.Area = area; | |
return this; | |
} | |
public ICanBuildRoutesBuilder ScanAssembly(string assemblyName) | |
{ | |
model.ScanAssembly(Assembly.Load(assemblyName)); | |
return this; | |
} | |
public ICanBuildRoutesBuilder ScanAssembly(Assembly assembly) | |
{ | |
model.ScanAssembly(assembly); | |
return this; | |
} | |
public ICanAssignRoutes WithActionNamesAsRoutes() | |
{ | |
if (model.AssembliesToScan == null || !model.AssembliesToScan.Any()) | |
throw new InvalidOperationException("You have not specified any assemblies to scan"); | |
model.RouteCreationConvention = new ActionNameRouteConvention(); | |
return this; | |
} | |
public ICanAssignRoutes WithActionNamesWithUnderscoresAsSeperatedRoutes() | |
{ | |
if (model.AssembliesToScan == null || !model.AssembliesToScan.Any()) | |
throw new InvalidOperationException("You have not specified any assemblies to scan"); | |
model.RouteCreationConvention = new ActionNameWithUnderscoresRouteConvention(); | |
return this; | |
} | |
public ICanAssignRoutes UsingAspNetMvcDefaults() | |
{ | |
if (model.AssembliesToScan == null || !model.AssembliesToScan.Any()) | |
throw new InvalidOperationException("You have not specified any assemblies to scan"); | |
model.RouteCreationConvention = new AspNetMvcRouteConvention(); | |
return this; | |
} | |
public ICanAssignRoutes UsingRouteGenerationConvention(IRouteGenerationConvention convention) | |
{ | |
if (model.AssembliesToScan == null || !model.AssembliesToScan.Any()) | |
throw new InvalidOperationException("You have not specified any assemblies to scan"); | |
model.RouteCreationConvention = convention; | |
return this; | |
} | |
public ICanAssignRoutes IgnoreAspNetMvcDefaultRoute(string controller, string action) | |
{ | |
model.IgnoreAspNetMvcDefaultRoute = true; | |
model.Controller = controller; | |
model.Action = action; | |
return this; | |
} | |
public void AssignRoutes(RouteCollection routes) | |
{ | |
if (model.RouteCreationConvention == null) | |
throw new InvalidOperationException("No route creation convention assigned"); | |
model.AssembliesToScan.ToList().ForEach(x => model.RouteCreationConvention.GetRoutes(x.GetTypes(), | |
new RouteGenerationOptions | |
{ | |
AreaName = model.Area, | |
IgnoreMvcDefaultRoute = model.IgnoreAspNetMvcDefaultRoute, | |
Controller = model.Controller, | |
Action = model.Action | |
}) | |
.ToList().ForEach(routes.Add)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment