public void Apply_EnrichesOperationMetadata_IfMinimalActionDecoratedWithSwaggerOperationAttribute() { var operationAttribute = new SwaggerOperationAttribute("Summary for ActionWithSwaggerOperationAttribute") { Description = "Description for ActionWithSwaggerOperationAttribute", OperationId = "actionWithSwaggerOperationAttribute", Tags = new[] { "foobar" } }; var action = RequestDelegateFactory.Create((string parameter) => "{}"); var operation = new OpenApiOperation(); var methodInfo = action.RequestDelegate.Method; var apiDescription = new ApiDescription() { ActionDescriptor = new ActionDescriptor() { EndpointMetadata = new[] { operationAttribute } } }; var filterContext = new OperationFilterContext( apiDescription: apiDescription, schemaRegistry: null, schemaRepository: null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); Assert.Equal(new[] { "foobar" }, operation.Tags.Cast <OpenApiTag>().Select(t => t.Name)); }
/// <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))); }
public async Task RequestDelegateInvokesAction(Delegate @delegate) { var httpContext = new DefaultHttpContext(); var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); Assert.True(httpContext.Items["invoked"] as bool?); }
public async Task RequestDelegatePopulatesFromRouteOptionalParameter() { var httpContext = new DefaultHttpContext(); var requestDelegate = RequestDelegateFactory.Create((Action <HttpContext, int>)TestAction); await requestDelegate(httpContext); Assert.Equal(42, httpContext.Items["input"]); }
public async Task RequestDelegatePopulatesFromRouteOptionalParameter(Delegate @delegate) { var httpContext = new DefaultHttpContext(); var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); Assert.Equal(42, httpContext.Items["input"] as int?); }
public async Task RequestDelegatePopulatesFromOptionalStringParameter() { var httpContext = new DefaultHttpContext(); var requestDelegate = RequestDelegateFactory.Create((Action <HttpContext, string>)TestOptionalString); await requestDelegate(httpContext); Assert.Equal("default", httpContext.Items["input"]); }
/// <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))); }
public async Task RequestDelegatePopulatesFromRouteOptionalParameterBasedOnParameterName() { const string paramName = "value"; const int originalRouteParam = 47; var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues[paramName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo); var requestDelegate = RequestDelegateFactory.Create((Action <HttpContext, int>)TestAction); await requestDelegate(httpContext); Assert.Equal(47, httpContext.Items["input"]); }
public async Task RequestDelegatePopulatesFromRouteParameterBasedOnParameterName(Delegate @delegate) { const string paramName = "value"; const int originalRouteParam = 42; var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues[paramName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo); var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); Assert.Equal(originalRouteParam, httpContext.Items["input"] as int?); }
/// <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))); }