/// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="action">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static MinimalActionEndpointConventionBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate action) { if (endpoints is null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern is null) { throw new ArgumentNullException(nameof(pattern)); } if (action is null) { throw new ArgumentNullException(nameof(action)); } const int defaultOrder = 0; var builder = new RouteEndpointBuilder( RequestDelegateFactory.Create(action, endpoints.ServiceProvider), pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), }; // REVIEW: Should we add an IActionMethodMetadata with just MethodInfo on it so we are // explicit about the MethodInfo representing the "action" and not the RequestDelegate? // Add MethodInfo as metadata to assist with OpenAPI generation for the endpoint. builder.Metadata.Add(action.Method); // Add delegate attributes as metadata var attributes = action.Method.GetCustomAttributes(); // This can be null if the delegate is a dynamic method or compiled from an expression tree if (attributes is not null) { foreach (var attribute in attributes) { builder.Metadata.Add(attribute); } } var dataSource = endpoints.DataSources.OfType <ModelEndpointDataSource>().FirstOrDefault(); if (dataSource is null) { dataSource = new ModelEndpointDataSource(); endpoints.DataSources.Add(dataSource); } return(new MinimalActionEndpointConventionBuilder(dataSource.AddEndpointBuilder(builder))); }
/// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="builder">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="displayName">The display name for the endpoint.</param> /// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param> /// <param name="metadata">Metadata that is added to the endpoint.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static IEndpointConventionBuilder Map( this IEndpointRouteBuilder builder, RoutePattern pattern, string displayName, RequestDelegate requestDelegate, params object[] metadata) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (pattern == null) { throw new ArgumentNullException(nameof(pattern)); } if (requestDelegate == null) { throw new ArgumentNullException(nameof(requestDelegate)); } const int defaultOrder = 0; var routeEndpointBuilder = new RouteEndpointBuilder( requestDelegate, pattern, defaultOrder) { DisplayName = displayName }; // Add delegate attributes as metadata foreach (var attribute in requestDelegate.Method.GetCustomAttributes()) { routeEndpointBuilder.Metadata.Add(attribute); } if (metadata != null) { foreach (var item in metadata) { routeEndpointBuilder.Metadata.Add(item); } } var modelEndpointDataSource = builder.DataSources.OfType <ModelEndpointDataSource>().FirstOrDefault(); if (modelEndpointDataSource == null) { modelEndpointDataSource = new ModelEndpointDataSource(); builder.DataSources.Add(modelEndpointDataSource); } return(modelEndpointDataSource.AddEndpointBuilder(routeEndpointBuilder)); }
/// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="action">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static MinimalActionEndpointConventionBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate action) { if (endpoints is null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern is null) { throw new ArgumentNullException(nameof(pattern)); } if (action is null) { throw new ArgumentNullException(nameof(action)); } const int defaultOrder = 0; var builder = new RouteEndpointBuilder( RequestDelegateFactory.Create(action), pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), }; // Add delegate attributes as metadata var attributes = action.Method.GetCustomAttributes(); // This can be null if the delegate is a dynamic method or compiled from an expression tree if (attributes is not null) { foreach (var attribute in attributes) { builder.Metadata.Add(attribute); } } var dataSource = endpoints.DataSources.OfType <ModelEndpointDataSource>().FirstOrDefault(); if (dataSource is null) { dataSource = new ModelEndpointDataSource(); endpoints.DataSources.Add(dataSource); } return(new MinimalActionEndpointConventionBuilder(dataSource.AddEndpointBuilder(builder))); }
/// <summary> /// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests /// for the specified pattern. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="handler">The delegate executed when the endpoint is matched.</param> /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns> public static DelegateEndpointConventionBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate handler) { if (endpoints is null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern is null) { throw new ArgumentNullException(nameof(pattern)); } if (handler is null) { throw new ArgumentNullException(nameof(handler)); } const int defaultOrder = 0; var routeParams = new List <string>(pattern.Parameters.Count); foreach (var part in pattern.Parameters) { routeParams.Add(part.Name); } var routeHandlerOptions = endpoints.ServiceProvider?.GetService <IOptions <RouteHandlerOptions> >(); var options = new RequestDelegateFactoryOptions { ServiceProvider = endpoints.ServiceProvider, RouteParameterNames = routeParams, ThrowOnBadRequest = routeHandlerOptions?.Value.ThrowOnBadRequest ?? false, }; var requestDelegateResult = RequestDelegateFactory.Create(handler, options); var builder = new RouteEndpointBuilder( requestDelegateResult.RequestDelegate, pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), }; // REVIEW: Should we add an IActionMethodMetadata with just MethodInfo on it so we are // explicit about the MethodInfo representing the "handler" and not the RequestDelegate? // Add MethodInfo as metadata to assist with OpenAPI generation for the endpoint. builder.Metadata.Add(handler.Method); // Methods defined in a top-level program are generated as statics so the delegate // target will be null. Inline lambdas are compiler generated method so they can // be filtered that way. if (GeneratedNameParser.TryParseLocalFunctionName(handler.Method.Name, out var endpointName) || !TypeHelper.IsCompilerGeneratedMethod(handler.Method)) { endpointName ??= handler.Method.Name; builder.Metadata.Add(new EndpointNameMetadata(endpointName)); builder.Metadata.Add(new RouteNameMetadata(endpointName)); builder.DisplayName = $"{builder.DisplayName} => {endpointName}"; } // Add delegate attributes as metadata var attributes = handler.Method.GetCustomAttributes(); // Add add request delegate metadata foreach (var metadata in requestDelegateResult.EndpointMetadata) { builder.Metadata.Add(metadata); } // This can be null if the delegate is a dynamic method or compiled from an expression tree if (attributes is not null) { foreach (var attribute in attributes) { builder.Metadata.Add(attribute); } } var dataSource = endpoints.DataSources.OfType <ModelEndpointDataSource>().FirstOrDefault(); if (dataSource is null) { dataSource = new ModelEndpointDataSource(); endpoints.DataSources.Add(dataSource); } return(new DelegateEndpointConventionBuilder(dataSource.AddEndpointBuilder(builder))); }
private static MapActionEndpointConventionBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate action, string?displayName) { if (endpoints is null) { throw new ArgumentNullException(nameof(endpoints)); } if (pattern is null) { throw new ArgumentNullException(nameof(pattern)); } if (action is null) { throw new ArgumentNullException(nameof(action)); } const int defaultOrder = 0; var builder = new RouteEndpointBuilder( MapActionExpressionTreeBuilder.BuildRequestDelegate(action), pattern, defaultOrder) { DisplayName = pattern.RawText ?? pattern.DebuggerToString(), }; // Add delegate attributes as metadata var attributes = action.Method.GetCustomAttributes(); string?routeName = null; int? routeOrder = null; // This can be null if the delegate is a dynamic method or compiled from an expression tree if (attributes is not null) { foreach (var attribute in attributes) { if (attribute is IRoutePatternMetadata patternMetadata && patternMetadata.RoutePattern is not null) { throw new InvalidOperationException($"'{attribute.GetType()}' implements {nameof(IRoutePatternMetadata)} which is not supported by this method."); } if (attribute is IHttpMethodMetadata methodMetadata && methodMetadata.HttpMethods.Any()) { throw new InvalidOperationException($"'{attribute.GetType()}' implements {nameof(IHttpMethodMetadata)} which is not supported by this method."); } if (attribute is IRouteNameMetadata nameMetadata && nameMetadata.RouteName is string name) { routeName = name; } if (attribute is IRouteOrderMetadata orderMetadata && orderMetadata.RouteOrder is int order) { routeOrder = order; } builder.Metadata.Add(attribute); } } builder.DisplayName = routeName ?? displayName ?? builder.DisplayName; builder.Order = routeOrder ?? defaultOrder; var dataSource = endpoints.DataSources.OfType <ModelEndpointDataSource>().FirstOrDefault(); if (dataSource is null) { dataSource = new ModelEndpointDataSource(); endpoints.DataSources.Add(dataSource); } return(new MapActionEndpointConventionBuilder(dataSource.AddEndpointBuilder(builder))); }