public void Descriptions_RecognizesCompositeRoutes() { var config = new HttpConfiguration(); var routeTemplate = "api/values"; var controllerDescriptor = new HttpControllerDescriptor( config, "AttributeApiExplorerValues", typeof(AttributeApiExplorerValuesController) ); var action = new ReflectedHttpActionDescriptor( controllerDescriptor, typeof(AttributeApiExplorerValuesController).GetMethod("Action") ); var actions = new ReflectedHttpActionDescriptor[] { action }; var routeCollection = new List <IHttpRoute>(); routeCollection.Add(CreateDirectRoute(routeTemplate, actions)); RouteCollectionRoute route = new RouteCollectionRoute(); route.EnsureInitialized(() => routeCollection); config.Routes.Add("Route", route); var descriptions = new ApiExplorer(config).ApiDescriptions; ApiDescription description = Assert.Single(descriptions); Assert.Equal(HttpMethod.Get, description.HttpMethod); Assert.Equal(routeTemplate, description.RelativePath); Assert.Equal(action, description.ActionDescriptor); }
/// <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> internal static void MapMvcAttributeRoutes(this RouteCollection routes, IEnumerable <Type> controllerTypes, IInlineConstraintResolver constraintResolver) { List <RouteEntry> routeEntries = new AttributeRoutingMapper(new RouteBuilder2(constraintResolver)).MapMvcAttributeRoutes(controllerTypes); // 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. RouteEntry[] sorted = routeEntries.OrderBy(r => r.Route.GetOrder()).ThenBy(r => r.Route.GetPrecedence()).ToArray(); RouteCollectionRoute aggregateRoute = new RouteCollectionRoute(); if (sorted.Length > 0) { routes.Add(aggregateRoute); } foreach (var routeEntry in sorted) { aggregateRoute.SubRoutes.Add(routeEntry.Name, routeEntry.Route); if (routeEntry.Name == null) { routes.Add(new GenerationRoute(routeEntry.Route)); } else { routes.Add(routeEntry.Name, new GenerationRoute(routeEntry.Route)); } } }
/// <summary> /// Maps the attribute-defined routes for the application. /// </summary> /// <param name="configuration">The server configuration.</param> /// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> to use for resolving inline constraints.</param> public static void MapHttpAttributeRoutes(this HttpConfiguration configuration, IInlineConstraintResolver constraintResolver) { HttpRouteBuilder routeBuilder = new HttpRouteBuilder(constraintResolver); var attrRoute = new RouteCollectionRoute(); configuration.Routes.Add(AttributeRouteName, attrRoute); 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); // 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 <HttpSubRouteCollection> initializer = () => MapHttpAttributeRoutesInternal(configuration, routeBuilder); // This won't change config. It wants to pick up the finalized config. HttpSubRouteCollection subRoutes = attrRoute.EnsureInitialized(initializer); if (subRoutes != null) { AddGenerationHooksForSubRoutes(config.Routes, subRoutes, routeBuilder); } }; }
public static RouteCollectionRoute BuildDirectRouteFromMethod <T>(Expression <Action <T> > methodCall) { var route = new RouteCollectionRoute(); AddDirectRouteFromMethod(route, methodCall); return(route); }
/// <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 static RouteCollectionRoute BuildDirectRouteFromController <T>() { RouteCollectionRoute route = new RouteCollectionRoute(); AddDirectRouteFromController <T>(route); return(route); }
public static void AddDirectRouteMatches( this RouteData routeData, Func <RouteBase, RouteData, bool> selector = null ) { RouteCollectionRoute route = (RouteCollectionRoute)routeData.Route; List <RouteData> matches = new List <RouteData>(); foreach (var subRoute in route) { RouteData match = new RouteData() { Route = subRoute }; bool isMatch = selector == null ? true : selector(subRoute, match); if (isMatch) { matches.Add(match); } } if (matches.Any()) { routeData.SetDirectRouteMatches(matches); } }
/// <summary> /// Maps the attribute-defined routes for the application. /// </summary> /// <param name="configuration">The server configuration.</param> /// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> to use for resolving inline constraints.</param> public static void MapHttpAttributeRoutes(this HttpConfiguration configuration, IInlineConstraintResolver constraintResolver) { HttpRouteBuilder routeBuilder = new HttpRouteBuilder(constraintResolver); var attrRoute = new RouteCollectionRoute(); configuration.Routes.Add(AttributeRouteName, attrRoute); 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); // 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<HttpSubRouteCollection> initializer = () => MapHttpAttributeRoutesInternal(configuration, routeBuilder); // This won't change config. It wants to pick up the finalized config. HttpSubRouteCollection subRoutes = attrRoute.EnsureInitialized(initializer); if (subRoutes != null) { AddGenerationHooksForSubRoutes(config.Routes, subRoutes, routeBuilder); } }; }
private static RouteData CreateRouteData(RouteBase route, RouteValueDictionary routeValues, RouteValueDictionary dataTokens, ViewContext parentViewContext) { RouteData routeData = new RouteData(); foreach (KeyValuePair <string, object> kvp in routeValues) { routeData.Values.Add(kvp.Key, kvp.Value); } foreach (KeyValuePair <string, object> kvp in dataTokens) { routeData.DataTokens.Add(kvp.Key, kvp.Value); } routeData.Route = route; routeData.DataTokens[ControllerContext.ParentActionViewContextToken] = parentViewContext; // It's possible that the outgoing route is a direct route - in which case it's not possible to reach using // the action name and controller name. We need to check for that case to determine if we need to create a // 'direct route' routedata to reach it. if (route.IsDirectRoute()) { return(RouteCollectionRoute.CreateDirectRouteMatch(route, new List <RouteData>() { routeData })); } else { return(routeData); } }
private ICollection <Route> GetAttributeRoutes(RouteCollection routes) { RouteCollectionRoute attributeRoute = routes.OfType <RouteCollectionRoute>().Single(); Assert.NotNull(attributeRoute); return(attributeRoute.SubRoutes); }
private static RouteData CreateRouteData( RouteBase route, RouteValueDictionary routeValues, RouteValueDictionary dataTokens, ViewContext parentViewContext ) { RouteData routeData = new RouteData(); foreach (KeyValuePair <string, object> kvp in routeValues) { routeData.Values.Add(kvp.Key, kvp.Value); } foreach (KeyValuePair <string, object> kvp in dataTokens) { routeData.DataTokens.Add(kvp.Key, kvp.Value); } routeData.Route = route; routeData.DataTokens[ControllerContext.ParentActionViewContextToken] = parentViewContext; // It's possible that the outgoing route is a direct route - in which case it's not possible to reach using // the action name and controller name. We need to check for that case to determine if we need to create a // 'direct route' routedata to reach it. if (route.IsDirectRoute()) { // Codeplex-2136 - ControllerContext.IsChildAction returns false inside Controller.Initialize() // // We're constructing a 'temp' route data to wrap the route data for the match we're invoking via // an attribute route. The ControllerContext will look at datatokens to see if it's being invoked // as a child action. // // By sticking the view context on both route data ControllerContext will do the right thing // at all parts of the pipeline. var directRouteData = RouteCollectionRoute.CreateDirectRouteMatch( route, new List <RouteData>() { routeData } ); directRouteData.DataTokens[ControllerContext.ParentActionViewContextToken] = parentViewContext; return(directRouteData); } else { return(routeData); } }
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); } }; }
public static void AddDirectRouteFromController <T>(this RouteBase routeBase) { RouteCollectionRoute route = (RouteCollectionRoute)routeBase; var controllerType = typeof(T); var entries = new AttributeRoutingMapper(new RouteBuilder2()).MapMvcAttributeRoutes(new Type[] { controllerType, }); foreach (var entry in entries) { route.SubRoutes.Add(entry.Route); } }
public static void AddDirectRouteFromMethod <T>(this RouteBase routeBase, Expression <Action <T> > methodCall) { RouteCollectionRoute route = (RouteCollectionRoute)routeBase; 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 }); subRoute.SetTargetControllerDescriptor(controllerDescriptor); route.SubRoutes.Add(subRoute); } }
public void Descriptions_RecognizesCompositeRoutes() { var config = new HttpConfiguration(); var routeTemplate = "api/values"; var controllerDescriptor = new HttpControllerDescriptor(config, "AttributeApiExplorerValues", typeof(AttributeApiExplorerValuesController)); var action = new ReflectedHttpActionDescriptor(controllerDescriptor, typeof(AttributeApiExplorerValuesController).GetMethod("Action")); var actions = new ReflectedHttpActionDescriptor[] { action }; var routeCollection = new HttpSubRouteCollection(); routeCollection.Add("testroute", new HttpDirectRoute(routeTemplate, 0, actions)); RouteCollectionRoute route = new RouteCollectionRoute(); route.EnsureInitialized(() => routeCollection); config.Routes.Add("Route", route); var descriptions = new ApiExplorer(config).ApiDescriptions; ApiDescription description = Assert.Single(descriptions); Assert.Equal(HttpMethod.Get, description.HttpMethod); Assert.Equal(routeTemplate, description.RelativePath); Assert.Equal(action, description.ActionDescriptor); }