/// <summary> /// Maps the attribute-defined routes for the application. /// </summary> /// <param name="routes"></param> /// <param name="controllerTypes">The controller types to scan.</param> /// <param name="constraintResolver"> /// The <see cref="IInlineConstraintResolver"/> to use for resolving inline constraints in route templates. /// </param> public static void MapAttributeRoutes(RouteCollection routes, IEnumerable<Type> controllerTypes, IInlineConstraintResolver constraintResolver) { SubRouteCollection subRoutes = new SubRouteCollection(); AddRouteEntries(subRoutes, controllerTypes, constraintResolver); IReadOnlyCollection<RouteEntry> entries = subRoutes.Entries; if (entries.Count > 0) { RouteCollectionRoute aggregrateRoute = new RouteCollectionRoute(subRoutes); routes.Add(aggregrateRoute); // This sort is here to enforce a static ordering for link generation using these routes. // We don't apply dynamic criteria like ActionSelectors on link generation, but we can use the static // ones. // // Routes to actions are placed first because they are considered more specific. A route to an action // will only match for link generation if the action name was supplied, so this is essential for // correctness. Without this a controller-level route could be 'greedy' and generate a link when // the action-level route was intended. RouteEntry[] sorted = entries .OrderBy(r => r.Route.GetOrder()) .ThenBy(r => r.Route.GetTargetIsAction() ? 0 : 1) .ThenBy(r => r.Route.GetPrecedence()) .ToArray(); AddGenerationHooksForSubRoutes(routes, sorted); } }
public void GetControllerType_WithMultipleDirectRouteControllers_ThrowsInvalidOperationException() { // Arrange var requestContext = new RequestContext(); requestContext.RouteData = new RouteData(); SubRouteCollection subRoutes = new SubRouteCollection(); DirectRouteTestHelpers.AddDirectRouteFromController <AttributeRouteAtControllerLevelController>( subRoutes ); DirectRouteTestHelpers.AddDirectRouteFromMethod <WithRoutingAttributeController>( subRoutes, c => c.Action() ); requestContext.RouteData.Route = new RouteCollectionRoute(subRoutes); requestContext.RouteData.AddDirectRouteMatches(); var controllerActivator = new Mock <IControllerActivator>(MockBehavior.Strict).Object; var activatorResolver = new Resolver <IControllerActivator>(); var factory = new DefaultControllerFactory( controllerActivator, activatorResolver, null ); // Act & Assert Assert.Throws <InvalidOperationException>( () => factory.GetControllerType(requestContext, controllerName: null) ); }
public static RouteCollectionRoute BuildDirectRouteFromMethod <T>(Expression <Action <T> > methodCall) { SubRouteCollection collector = new SubRouteCollection(); AddDirectRouteFromMethod(collector, methodCall); return(new RouteCollectionRoute(collector)); }
public static RouteCollectionRoute BuildDirectRouteFromController <T>() { SubRouteCollection collector = new SubRouteCollection(); AddDirectRouteFromController <T>(collector); return(new RouteCollectionRoute(collector)); }
public static void MapAttributeRoutes( HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider ) { if (configuration == null) { throw new ArgumentNullException("configuration"); } if (constraintResolver == null) { throw new ArgumentNullException("constraintResolver"); } if (directRouteProvider == null) { throw new ArgumentNullException("directRouteProvider"); } RouteCollectionRoute aggregateRoute = new RouteCollectionRoute(); configuration.Routes.Add(AttributeRouteName, aggregateRoute); Action <HttpConfiguration> previousInitializer = configuration.Initializer; configuration.Initializer = config => { // Chain to the previous initializer hook. Do this before we access the config since // initialization may make last minute changes to the configuration. previousInitializer(config); SubRouteCollection subRoutes = null; // Add a single placeholder route that handles all of attribute routing. // Add an initialize hook that initializes these routes after the config has been initialized. Func <SubRouteCollection> initializer = () => { subRoutes = new SubRouteCollection(); AddRouteEntries( subRoutes, configuration, constraintResolver, directRouteProvider ); return(subRoutes); }; // This won't change config. It wants to pick up the finalized config. aggregateRoute.EnsureInitialized(initializer); if (subRoutes != null) { AddGenerationHooksForSubRoutes(config.Routes, subRoutes.Entries); } }; }
private static void AddRouteEntries(SubRouteCollection collector, string prefix, IReadOnlyCollection <IDirectRouteFactory> factories, IReadOnlyCollection <HttpActionDescriptor> actions, IInlineConstraintResolver constraintResolver, bool targetIsAction) { foreach (IDirectRouteFactory factory in factories) { RouteEntry entry = CreateRouteEntry(prefix, factory, actions, constraintResolver, targetIsAction); collector.Add(entry); } }
public static void AddDirectRouteFromController <T>(SubRouteCollection collector) { var controllerType = typeof(T); AttributeRoutingMapper.AddRouteEntries( collector, new Type[] { controllerType }, new DefaultInlineConstraintResolver(), new DefaultDirectRouteProvider()); }
public static void MapAttributeRoutes( HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider) { if (configuration == null) { throw new ArgumentNullException("configuration"); } if (constraintResolver == null) { throw new ArgumentNullException("constraintResolver"); } if (directRouteProvider == null) { throw new ArgumentNullException("directRouteProvider"); } RouteCollectionRoute aggregateRoute = new RouteCollectionRoute(); configuration.Routes.Add(AttributeRouteName, aggregateRoute); Action<HttpConfiguration> previousInitializer = configuration.Initializer; configuration.Initializer = config => { // Chain to the previous initializer hook. Do this before we access the config since // initialization may make last minute changes to the configuration. previousInitializer(config); SubRouteCollection subRoutes = null; // Add a single placeholder route that handles all of attribute routing. // Add an initialize hook that initializes these routes after the config has been initialized. Func<SubRouteCollection> initializer = () => { subRoutes = new SubRouteCollection(); AddRouteEntries(subRoutes, configuration, constraintResolver, directRouteProvider); return subRoutes; }; // This won't change config. It wants to pick up the finalized config. aggregateRoute.EnsureInitialized(initializer); if (subRoutes != null) { AddGenerationHooksForSubRoutes(config.Routes, subRoutes.Entries); } }; }
private static void AddRouteEntries(SubRouteCollection collector, HttpControllerDescriptor controller, IInlineConstraintResolver constraintResolver) { IHttpActionSelector actionSelector = controller.Configuration.Services.GetActionSelector(); ILookup <string, HttpActionDescriptor> actionMap = actionSelector.GetActionMapping(controller); if (actionMap == null) { return; } string prefix = GetRoutePrefix(controller); List <ReflectedHttpActionDescriptor> actionsWithoutRoutes = new List <ReflectedHttpActionDescriptor>(); foreach (IGrouping <string, HttpActionDescriptor> actionGrouping in actionMap) { foreach (ReflectedHttpActionDescriptor action in actionGrouping.OfType <ReflectedHttpActionDescriptor>()) { IReadOnlyCollection <IDirectRouteFactory> factories = GetRouteFactories(action); // Ignore the Route attributes from inherited actions. if (action.MethodInfo != null && action.MethodInfo.DeclaringType != controller.ControllerType) { factories = null; } if (factories != null && factories.Count > 0) { AddRouteEntries(collector, prefix, factories, new ReflectedHttpActionDescriptor[] { action }, constraintResolver, targetIsAction: true); } else { // IF there are no routes on the specific action, attach it to the controller routes (if any). actionsWithoutRoutes.Add(action); } } } IReadOnlyCollection <IDirectRouteFactory> controllerFactories = GetRouteFactories(controller); // If they exist and have not been overridden, create routes for controller-level route providers. if (controllerFactories.Count > 0 && actionsWithoutRoutes.Count > 0) { AddRouteEntries(collector, prefix, controllerFactories, actionsWithoutRoutes, constraintResolver, targetIsAction: false); } }
public static void AddDirectRouteFromMethod <T>(SubRouteCollection collector, Expression <Action <T> > methodCall) { var method = ((MethodCallExpression)methodCall.Body).Method; var attributes = method.GetCustomAttributes(false).OfType <IRouteInfoProvider>(); var controllerDescriptor = new ReflectedAsyncControllerDescriptor(method.DeclaringType); var actionDescriptor = new ReflectedActionDescriptor(method, method.Name, controllerDescriptor); foreach (var attribute in attributes) { var subRoute = new Route(attribute.Template, routeHandler: null); subRoute.SetTargetActionDescriptors(new ActionDescriptor[] { actionDescriptor }); collector.Add(new RouteEntry(null, subRoute)); } }
private static void AddRouteEntries(SubRouteCollection collector, HttpConfiguration configuration, IInlineConstraintResolver constraintResolver) { Contract.Assert(configuration != null); IHttpControllerSelector controllerSelector = configuration.Services.GetHttpControllerSelector(); IDictionary <string, HttpControllerDescriptor> controllerMap = controllerSelector.GetControllerMapping(); if (controllerMap != null) { foreach (HttpControllerDescriptor controllerDescriptor in controllerMap.Values) { AddRouteEntries(collector, controllerDescriptor, constraintResolver); } } }
public void SubRouteCollection_Throws_OnDuplicateNamedRoute_MVC() { // Arrange var collection = new SubRouteCollection(); var route1 = new Route("Home/Index", new Mock<IRouteHandler>().Object); var route2 = new Route("Person/Index", new Mock<IRouteHandler>().Object); collection.Add(new RouteEntry("route", route1)); var expectedError = "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" + "Duplicates:" + Environment.NewLine + "Person/Index" + Environment.NewLine + "Home/Index"; // Act & Assert Assert.Throws<InvalidOperationException>(() => collection.Add(new RouteEntry("route", route2)), expectedError); }
public void SubRouteCollection_Throws_OnDuplicateNamedRoute_WebAPI() { // Arrange var collection = new SubRouteCollection(); var route1 = new HttpRoute("api/Person"); var route2 = new HttpRoute("api/Car"); collection.Add(new RouteEntry("route", route1)); var expectedError = "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" + "Duplicates:" + Environment.NewLine + "api/Car" + Environment.NewLine + "api/Person"; // Act & Assert Assert.Throws<InvalidOperationException>(() => collection.Add(new RouteEntry("route", route2)), expectedError); }
public void SubRouteCollection_Throws_OnDuplicateNamedRoute_WebAPI() { // Arrange var collection = new SubRouteCollection(); var route1 = new HttpRoute("api/Person"); var route2 = new HttpRoute("api/Car"); collection.Add(new RouteEntry("route", route1)); var expectedError = "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" + "Duplicates:" + Environment.NewLine + "api/Car" + Environment.NewLine + "api/Person"; // Act & Assert Assert.Throws <InvalidOperationException>(() => collection.Add(new RouteEntry("route", route2)), expectedError); }
public void SubRouteCollection_Throws_OnDuplicateNamedRoute_MVC() { // Arrange var collection = new SubRouteCollection(); var route1 = new Route("Home/Index", new Mock <IRouteHandler>().Object); var route2 = new Route("Person/Index", new Mock <IRouteHandler>().Object); collection.Add(new RouteEntry("route", route1)); var expectedError = "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" + "Duplicates:" + Environment.NewLine + "Person/Index" + Environment.NewLine + "Home/Index"; // Act & Assert Assert.Throws <InvalidOperationException>(() => collection.Add(new RouteEntry("route", route2)), expectedError); }
internal static void AddRouteEntries(SubRouteCollection collector, IEnumerable<Type> controllerTypes, IInlineConstraintResolver constraintResolver) { ControllerDescriptorCache descriptorsCache = new AsyncControllerActionInvoker().DescriptorCache; IEnumerable<ReflectedAsyncControllerDescriptor> descriptors = controllerTypes .Select( type => descriptorsCache.GetDescriptor(type, innerType => new ReflectedAsyncControllerDescriptor(innerType), type)) .Cast<ReflectedAsyncControllerDescriptor>(); foreach (ReflectedAsyncControllerDescriptor controllerDescriptor in descriptors) { AddRouteEntries(collector, controllerDescriptor, constraintResolver); } }
private static void AddRouteEntries( SubRouteCollection collector, HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider) { Contract.Assert(configuration != null); Contract.Assert(directRouteProvider != null); IHttpControllerSelector controllerSelector = configuration.Services.GetHttpControllerSelector(); IDictionary<string, HttpControllerDescriptor> controllerMap = controllerSelector.GetControllerMapping(); if (controllerMap != null) { foreach (HttpControllerDescriptor controllerDescriptor in controllerMap.Values) { IHttpActionSelector actionSelector = controllerDescriptor.Configuration.Services.GetActionSelector(); ILookup<string, HttpActionDescriptor> actionsByName = actionSelector.GetActionMapping(controllerDescriptor); if (actionsByName == null) { continue; } List<HttpActionDescriptor> actions = actionsByName.SelectMany(g => g).ToList(); IReadOnlyCollection<RouteEntry> newEntries = directRouteProvider.GetDirectRoutes(controllerDescriptor, actions, constraintResolver); if (newEntries == null) { throw Error.InvalidOperation( SRResources.TypeMethodMustNotReturnNull, typeof(IDirectRouteProvider).Name, "GetDirectRoutes"); } foreach (RouteEntry entry in newEntries) { if (entry == null) { throw Error.InvalidOperation( SRResources.TypeMethodMustNotReturnNull, typeof(IDirectRouteProvider).Name, "GetDirectRoutes"); } DirectRouteBuilder.ValidateRouteEntry(entry); // We need to mark each action as only reachable by direct routes so that traditional routes // don't accidentally hit them. HttpControllerDescriptor routeControllerDescriptor = entry.Route.GetTargetControllerDescriptor(); if (routeControllerDescriptor == null) { HttpActionDescriptor[] actionDescriptors = entry.Route.GetTargetActionDescriptors(); foreach (var actionDescriptor in actionDescriptors) { actionDescriptor.SetIsAttributeRouted(true); } } else { routeControllerDescriptor.SetIsAttributeRouted(true); } } collector.AddRange(newEntries); } } }
private static void AddRouteEntries( SubRouteCollection collector, HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider) { Contract.Assert(configuration != null); Contract.Assert(directRouteProvider != null); IHttpControllerSelector controllerSelector = configuration.Services.GetHttpControllerSelector(); IDictionary <string, HttpControllerDescriptor> controllerMap = controllerSelector.GetControllerMapping(); if (controllerMap != null) { foreach (HttpControllerDescriptor controllerDescriptor in controllerMap.Values) { IHttpActionSelector actionSelector = controllerDescriptor.Configuration.Services.GetActionSelector(); ILookup <string, HttpActionDescriptor> actionsByName = actionSelector.GetActionMapping(controllerDescriptor); if (actionsByName == null) { continue; } List <HttpActionDescriptor> actions = actionsByName.SelectMany(g => g).ToList(); IReadOnlyCollection <RouteEntry> newEntries = directRouteProvider.GetDirectRoutes(controllerDescriptor, actions, constraintResolver); if (newEntries == null) { throw Error.InvalidOperation( SRResources.TypeMethodMustNotReturnNull, typeof(IDirectRouteProvider).Name, "GetDirectRoutes"); } foreach (RouteEntry entry in newEntries) { if (entry == null) { throw Error.InvalidOperation( SRResources.TypeMethodMustNotReturnNull, typeof(IDirectRouteProvider).Name, "GetDirectRoutes"); } DirectRouteBuilder.ValidateRouteEntry(entry); // We need to mark each action as only reachable by direct routes so that traditional routes // don't accidentally hit them. HttpControllerDescriptor routeControllerDescriptor = entry.Route.GetTargetControllerDescriptor(); if (routeControllerDescriptor == null) { HttpActionDescriptor[] actionDescriptors = entry.Route.GetTargetActionDescriptors(); foreach (var actionDescriptor in actionDescriptors) { actionDescriptor.SetIsAttributeRouted(true); } } else { routeControllerDescriptor.SetIsAttributeRouted(true); } } collector.AddRange(newEntries); } } }
private static void AddRouteEntries(SubRouteCollection collector, HttpConfiguration configuration, IInlineConstraintResolver constraintResolver) { Contract.Assert(configuration != null); IHttpControllerSelector controllerSelector = configuration.Services.GetHttpControllerSelector(); IDictionary<string, HttpControllerDescriptor> controllerMap = controllerSelector.GetControllerMapping(); if (controllerMap != null) { foreach (HttpControllerDescriptor controllerDescriptor in controllerMap.Values) { AddRouteEntries(collector, controllerDescriptor, constraintResolver); } } }
private static void AddRouteEntries(SubRouteCollection collector, HttpControllerDescriptor controller, IInlineConstraintResolver constraintResolver) { IHttpActionSelector actionSelector = controller.Configuration.Services.GetActionSelector(); ILookup<string, HttpActionDescriptor> actionMap = actionSelector.GetActionMapping(controller); if (actionMap == null) { return; } string prefix = GetRoutePrefix(controller); List<ReflectedHttpActionDescriptor> actionsWithoutRoutes = new List<ReflectedHttpActionDescriptor>(); foreach (IGrouping<string, HttpActionDescriptor> actionGrouping in actionMap) { foreach (ReflectedHttpActionDescriptor action in actionGrouping.OfType<ReflectedHttpActionDescriptor>()) { IReadOnlyCollection<IDirectRouteFactory> factories = GetRouteFactories(action); // Ignore the Route attributes from inherited actions. if (action.MethodInfo != null && action.MethodInfo.DeclaringType != controller.ControllerType) { factories = null; } if (factories != null && factories.Count > 0) { AddRouteEntries(collector, prefix, factories, new ReflectedHttpActionDescriptor[] { action }, constraintResolver, targetIsAction: true); } else { // IF there are no routes on the specific action, attach it to the controller routes (if any). actionsWithoutRoutes.Add(action); } } } IReadOnlyCollection<IDirectRouteFactory> controllerFactories = GetRouteFactories(controller); // If they exist and have not been overridden, create routes for controller-level route providers. if (controllerFactories.Count > 0 && actionsWithoutRoutes.Count > 0) { AddRouteEntries(collector, prefix, controllerFactories, actionsWithoutRoutes, constraintResolver, targetIsAction: false); } }
public static void AddDirectRouteFromController <T>(SubRouteCollection collector) { var controllerType = typeof(T); new AttributeRoutingMapper(new RouteBuilder2()).AddRouteEntries(collector, new Type[] { controllerType }); }
// just for testing internal static IReadOnlyCollection<RouteEntry> GetAttributeRoutes(Type controllerType) { SubRouteCollection collector = new SubRouteCollection(); AddRouteEntries( collector, new Type[] { controllerType }, new DefaultInlineConstraintResolver(), new DefaultDirectRouteProvider()); return collector.Entries; }
private static void AddRouteEntries(SubRouteCollection collector, string areaPrefix, string prefix, IEnumerable<IDirectRouteFactory> factories, IReadOnlyCollection<ActionDescriptor> actions, IInlineConstraintResolver constraintResolver, bool targetIsAction) { foreach (IDirectRouteFactory factory in factories) { RouteEntry entry = CreateRouteEntry(areaPrefix, prefix, factory, actions, constraintResolver, targetIsAction); collector.Add(entry); } }
internal static void AddRouteEntries(SubRouteCollection collector, ReflectedAsyncControllerDescriptor controller, IInlineConstraintResolver constraintResolver) { string prefix = GetRoutePrefix(controller); RouteAreaAttribute area = controller.GetAreaFrom(); string areaName = controller.GetAreaName(area); string areaPrefix = area != null ? area.AreaPrefix ?? area.AreaName : null; ValidateAreaPrefixTemplate(areaPrefix, areaName, controller); AsyncActionMethodSelector actionSelector = controller.Selector; foreach (var method in actionSelector.DirectRouteMethods) { ActionDescriptor action = CreateActionDescriptor(controller, actionSelector, method); IEnumerable<IDirectRouteFactory> factories = GetRouteFactories(method, controller.ControllerType); AddRouteEntries(collector, areaPrefix, prefix, factories, new ActionDescriptor[] { action }, constraintResolver, targetIsAction: true); } // Check for controller-level routes. List<ActionDescriptor> actionsWithoutRoutes = new List<ActionDescriptor>(); foreach (var method in actionSelector.StandardRouteMethods) { ActionDescriptor action = CreateActionDescriptor(controller, actionSelector, method); actionsWithoutRoutes.Add(action); } IReadOnlyCollection<IDirectRouteFactory> controllerFactories = GetRouteFactories(controller); // If they exist and have not been overridden, create routes for controller-level route providers. if (controllerFactories.Count > 0 && actionsWithoutRoutes.Count > 0) { AddRouteEntries(collector, areaPrefix, prefix, controllerFactories, actionsWithoutRoutes, constraintResolver, targetIsAction: false); } }
internal static void AddRouteEntries(SubRouteCollection collector, IEnumerable<Type> controllerTypes, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider) { IEnumerable<ReflectedAsyncControllerDescriptor> controllers = GetControllerDescriptors(controllerTypes); foreach (ReflectedAsyncControllerDescriptor controller in controllers) { List<ActionDescriptor> actions = GetActionDescriptors(controller); IReadOnlyCollection<RouteEntry> entries = directRouteProvider.GetDirectRoutes(controller, actions, constraintResolver); if (entries == null) { throw Error.InvalidOperation( MvcResources.TypeMethodMustNotReturnNull, typeof(IDirectRouteProvider).Name, "GetDirectRoutes"); } foreach (RouteEntry entry in entries) { if (entry == null) { throw Error.InvalidOperation( MvcResources.TypeMethodMustNotReturnNull, typeof(IDirectRouteProvider).Name, "GetDirectRoutes"); } DirectRouteBuilder.ValidateRouteEntry(entry); // This marks the action/controller as unreachable via traditional routing. if (entry.Route.GetTargetIsAction()) { var actionDescriptors = entry.Route.GetTargetActionDescriptors(); Contract.Assert(actionDescriptors != null && actionDescriptors.Any()); foreach (var actionDescriptor in actionDescriptors.OfType<IMethodInfoActionDescriptor>()) { var methodInfo = actionDescriptor.MethodInfo; if (methodInfo != null) { controller.Selector.StandardRouteMethods.Remove(methodInfo); } } } else { // This is a controller-level route - no actions in this controller are reachable via // traditional routes. controller.Selector.StandardRouteMethods.Clear(); } } collector.AddRange(entries); } }
public void GetControllerType_WithMultipleDirectRouteControllers_ThrowsInvalidOperationException() { // Arrange var requestContext = new RequestContext(); requestContext.RouteData = new RouteData(); SubRouteCollection subRoutes = new SubRouteCollection(); DirectRouteTestHelpers.AddDirectRouteFromController<AttributeRouteAtControllerLevelController>(subRoutes); DirectRouteTestHelpers.AddDirectRouteFromMethod<WithRoutingAttributeController>(subRoutes, c => c.Action()); requestContext.RouteData.Route = new RouteCollectionRoute(subRoutes); requestContext.RouteData.AddDirectRouteMatches(); var controllerActivator = new Mock<IControllerActivator>(MockBehavior.Strict).Object; var activatorResolver = new Resolver<IControllerActivator>(); var factory = new DefaultControllerFactory(controllerActivator, activatorResolver, null); // Act & Assert Assert.Throws<InvalidOperationException>(() => factory.GetControllerType(requestContext, controllerName: null)); }
internal static IReadOnlyCollection<RouteEntry> MapAttributeRoutes( ReflectedAsyncControllerDescriptor controller) { SubRouteCollection collector = new SubRouteCollection(); AddRouteEntries(collector, controller, new DefaultInlineConstraintResolver()); return collector.Entries; }