protected override List <Endpoint> CreateEndpoints(IReadOnlyList <ActionDescriptor> actions, IReadOnlyList <Action <EndpointBuilder> > conventions) { var endpoints = new List <Endpoint>(); foreach (var action in actions) { var attributeInfo = action.AttributeRouteInfo; if (attributeInfo == null) //Conventional Routing { foreach (var route in _conventionalRoutes) { var pattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues); if (pattern != null) { RouteEndpointBuilder builder = new RouteEndpointBuilder(_requestDelegate, pattern, route.Order); builder.Metadata.Add(action); endpoints.Add(builder.Build()); } } } else //Attribute Routing { var original = RoutePatternFactory.Parse(attributeInfo.Template); var pattern = _routePatternTransformer.SubstituteRequiredValues(original, action.RouteValues); if (pattern != null) { RouteEndpointBuilder builder = new RouteEndpointBuilder(_requestDelegate, pattern, attributeInfo.Order); builder.Metadata.Add(action); endpoints.Add(builder.Build()); } } } return(endpoints); }
private List <Endpoint> BuildEndpoints() { List <Endpoint> endpoints = new List <Endpoint>(); foreach (var hubMethod in HubMethods) { var requiredValues = new { hub = hubMethod.Hub, method = hubMethod.Method }; var order = 1; foreach (var pattern in Patterns) { var resolvedPattern = _routePatternTransformer.SubstituteRequiredValues(pattern, requiredValues); if (resolvedPattern == null) { continue; } var endpointModel = new RouteEndpointModel( hubMethod.RequestDelegate, resolvedPattern, order++); endpointModel.DisplayName = $"{hubMethod.Hub}.{hubMethod.Method}"; foreach (var convention in _conventions) { convention(endpointModel); } endpoints.Add(endpointModel.Build()); } } return(endpoints); }
public MvcEndpointDataSource(IActionInvokerFactory actionInvokerFactory, IActionDescriptorProvider actionDiscriptorProvider, RoutePatternTransformer transformer) { _actionInvokerFactory = actionInvokerFactory; _endpoints = new Lazy <IReadOnlyList <Endpoint> >(Create()); List <Endpoint> Create() { var Descriptors = actionDiscriptorProvider.GetActionDescriptors; return(Descriptors.Select(CreateEndpoint).ToList()); } Endpoint CreateEndpoint(ActionDescriptor actionDiscriptor) { var routePattern = RoutePatternFactory.Parse(actionDiscriptor.RouteInfo.Template); var newRoutePattern = transformer.SubstituteRequiredValues(routePattern, new Dictionary <string, string> { ["controller"] = actionDiscriptor.ControllerName, ["action"] = actionDiscriptor.ActionName }); routePattern = routePattern ?? newRoutePattern; var endPointBuilder = new RouteEndpointBuilder(InvokeAsync, routePattern, actionDiscriptor.RouteInfo.Order ?? 0); endPointBuilder.Metadata.Add(actionDiscriptor); return(endPointBuilder.Build()); } }
public void AddEndpoints( List <Endpoint> endpoints, HashSet <string> routeNames, ActionDescriptor action, IReadOnlyList <ConventionalRouteEntry> routes, IReadOnlyList <Action <EndpointBuilder> > conventions, bool createInertEndpoints) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (routeNames == null) { throw new ArgumentNullException(nameof(routeNames)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } if (routes == null) { throw new ArgumentNullException(nameof(routes)); } if (conventions == null) { throw new ArgumentNullException(nameof(conventions)); } if (createInertEndpoints) { var builder = new InertEndpointBuilder() { DisplayName = action.DisplayName, RequestDelegate = _requestDelegate, }; AddActionDataToBuilder( builder, routeNames, action, routeName: null, dataTokens: null, suppressLinkGeneration: false, suppressPathMatching: false, conventions, Array.Empty <Action <EndpointBuilder> >()); endpoints.Add(builder.Build()); } if (action.AttributeRouteInfo?.Template == null) { // Check each of the conventional patterns to see if the action would be reachable. // If the action and pattern are compatible then create an endpoint with action // route values on the pattern. foreach (var route in routes) { // A route is applicable if: // 1. It has a parameter (or default value) for 'required' non-null route value // 2. It does not have a parameter (or default value) for 'required' null route value var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues); if (updatedRoutePattern == null) { continue; } var requestDelegate = CreateRequestDelegate(action, route.DataTokens) ?? _requestDelegate; // We suppress link generation for each conventionally routed endpoint. We generate a single endpoint per-route // to handle link generation. var builder = new RouteEndpointBuilder(requestDelegate, updatedRoutePattern, route.Order) { DisplayName = action.DisplayName, }; AddActionDataToBuilder( builder, routeNames, action, route.RouteName, route.DataTokens, suppressLinkGeneration: true, suppressPathMatching: false, conventions, route.Conventions); endpoints.Add(builder.Build()); } } else { var requestDelegate = CreateRequestDelegate(action) ?? _requestDelegate; var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template); // Modify the route and required values to ensure required values can be successfully subsituted. // Subsitituting required values into an attribute route pattern should always succeed. var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern); var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues); if (updatedRoutePattern == null) { // This kind of thing can happen when a route pattern uses a *reserved* route value such as `action`. // See: https://github.com/dotnet/aspnetcore/issues/14789 var formattedRouteKeys = string.Join(", ", resolvedRouteValues.Keys.Select(k => $"'{k}'")); throw new InvalidOperationException( $"Failed to update the route pattern '{resolvedRoutePattern.RawText}' with required route values. " + $"This can occur when the route pattern contains parameters with reserved names such as: {formattedRouteKeys} " + $"and also uses route constraints such as '{{action:int}}'. " + "To fix this error, choose a different parameter name."); } var builder = new RouteEndpointBuilder(requestDelegate, updatedRoutePattern, action.AttributeRouteInfo.Order) { DisplayName = action.DisplayName, }; AddActionDataToBuilder( builder, routeNames, action, action.AttributeRouteInfo.Name, dataTokens: null, action.AttributeRouteInfo.SuppressLinkGeneration, action.AttributeRouteInfo.SuppressPathMatching, conventions, perRouteConventions: Array.Empty <Action <EndpointBuilder> >()); endpoints.Add(builder.Build()); } }
public void AddEndpoints( List <Endpoint> endpoints, HashSet <string> routeNames, ActionDescriptor action, IReadOnlyList <ConventionalRouteEntry> routes, IReadOnlyList <Action <EndpointBuilder> > conventions) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (routeNames == null) { throw new ArgumentNullException(nameof(routeNames)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } if (routes == null) { throw new ArgumentNullException(nameof(routes)); } if (conventions == null) { throw new ArgumentNullException(nameof(conventions)); } if (action.AttributeRouteInfo == null) { // Check each of the conventional patterns to see if the action would be reachable. // If the action and pattern are compatible then create an endpoint with action // route values on the pattern. foreach (var route in routes) { // A route is applicable if: // 1. It has a parameter (or default value) for 'required' non-null route value // 2. It does not have a parameter (or default value) for 'required' null route value var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues); if (updatedRoutePattern == null) { continue; } // We suppress link generation for each conventionally routed endpoint. We generate a single endpoint per-route // to handle link generation. var builder = CreateEndpoint( routeNames, action, updatedRoutePattern, route.RouteName, route.Order, route.DataTokens, suppressLinkGeneration: true, suppressPathMatching: false, conventions, route.Conventions); endpoints.Add(builder); } } else { var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template); // Modify the route and required values to ensure required values can be successfully subsituted. // Subsitituting required values into an attribute route pattern should always succeed. var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern); var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues); if (updatedRoutePattern == null) { throw new InvalidOperationException("Failed to update route pattern with required values."); } var endpoint = CreateEndpoint( routeNames, action, updatedRoutePattern, action.AttributeRouteInfo.Name, action.AttributeRouteInfo.Order, dataTokens: null, action.AttributeRouteInfo.SuppressLinkGeneration, action.AttributeRouteInfo.SuppressPathMatching, conventions, perRouteConventions: Array.Empty <Action <EndpointBuilder> >()); endpoints.Add(endpoint); } }
public void AddEndpoints( List <Endpoint> endpoints, ActionDescriptor action, IReadOnlyList <ConventionalRouteEntry> routes, IReadOnlyList <Action <EndpointBuilder> > conventions) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (action == null) { throw new ArgumentNullException(nameof(action)); } if (routes == null) { throw new ArgumentNullException(nameof(routes)); } if (conventions == null) { throw new ArgumentNullException(nameof(conventions)); } if (action.AttributeRouteInfo == null) { // In traditional conventional routing setup, the routes defined by a user have a static order // defined by how they are added into the list. We would like to maintain the same order when building // up the endpoints too. // // Start with an order of '1' for conventional routes as attribute routes have a default order of '0'. // This is for scenarios dealing with migrating existing Router based code to Endpoint Routing world. var conventionalRouteOrder = 1; // Check each of the conventional patterns to see if the action would be reachable. // If the action and pattern are compatible then create an endpoint with action // route values on the pattern. foreach (var route in routes) { // A route is applicable if: // 1. It has a parameter (or default value) for 'required' non-null route value // 2. It does not have a parameter (or default value) for 'required' null route value var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues); if (updatedRoutePattern == null) { continue; } var builder = CreateEndpoint( action, updatedRoutePattern, route.RouteName, conventionalRouteOrder++, route.DataTokens, suppressLinkGeneration: false, suppressPathMatching: false, conventions); endpoints.Add(builder); } } else { var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template); // Modify the route and required values to ensure required values can be successfully subsituted. // Subsitituting required values into an attribute route pattern should always succeed. var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern); var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues); if (updatedRoutePattern == null) { throw new InvalidOperationException("Failed to update route pattern with required values."); } var endpoint = CreateEndpoint( action, updatedRoutePattern, action.AttributeRouteInfo.Name, action.AttributeRouteInfo.Order, dataTokens: null, action.AttributeRouteInfo.SuppressLinkGeneration, action.AttributeRouteInfo.SuppressPathMatching, conventions); endpoints.Add(endpoint); } }
private void UpdateEndpoints() { lock (_lock) { var endpoints = new List <Endpoint>(); foreach (var action in _actions.ActionDescriptors.Items) { if (action.AttributeRouteInfo == null) { // In traditional conventional routing setup, the routes defined by a user have a static order // defined by how they are added into the list. We would like to maintain the same order when building // up the endpoints too. // // Start with an order of '1' for conventional routes as attribute routes have a default order of '0'. // This is for scenarios dealing with migrating existing Router based code to Endpoint Routing world. var conventionalRouteOrder = 1; // Check each of the conventional patterns to see if the action would be reachable. // If the action and pattern are compatible then create an endpoint with action // route values on the pattern. foreach (var endpointInfo in ConventionalEndpointInfos) { // An 'endpointInfo' is applicable if: // 1. It has a parameter (or default value) for 'required' non-null route value // 2. It does not have a parameter (or default value) for 'required' null route value var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(endpointInfo.ParsedPattern, action.RouteValues); if (updatedRoutePattern == null) { continue; } var endpoint = CreateEndpoint( action, updatedRoutePattern, endpointInfo.Name, conventionalRouteOrder++, endpointInfo.DataTokens, false, false, endpointInfo.Conventions); endpoints.Add(endpoint); } } else { var conventionBuilder = ResolveActionConventionBuilder(action); if (conventionBuilder == null) { // No convention builder for this action // Do not create an endpoint for it continue; } var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template); // Modify the route and required values to ensure required values can be successfully subsituted. // Subsitituting required values into an attribute route pattern should always succeed. var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern); var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues); if (updatedRoutePattern == null) { throw new InvalidOperationException("Failed to update route pattern with required values."); } var endpoint = CreateEndpoint( action, updatedRoutePattern, action.AttributeRouteInfo.Name, action.AttributeRouteInfo.Order, dataTokens: null, action.AttributeRouteInfo.SuppressLinkGeneration, action.AttributeRouteInfo.SuppressPathMatching, conventionBuilder.Conventions); endpoints.Add(endpoint); } } // See comments in DefaultActionDescriptorCollectionProvider. These steps are done // in a specific order to ensure callers always see a consistent state. // Step 1 - capture old token var oldCancellationTokenSource = _cancellationTokenSource; // Step 2 - update endpoints _endpoints = endpoints; // Step 3 - create new change token _cancellationTokenSource = new CancellationTokenSource(); _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token); // Step 4 - trigger old token oldCancellationTokenSource?.Cancel(); } }