Skip to content

Expose entire EndpointBuilder to filter factories #43000

@halter73

Description

@halter73

Background and Motivation

In reaction to #42592, I think we should expose the entire EndopintBuilder via RequestDelegateFactoryOptions. This would allow downcasting to RouteEndpointBuilder and seeing the RoutePattern too from filters.

Proposed API

namespace Microsoft.AspNetCore.Http;

public sealed class RequestDelegateFactoryOptions
{
-    public IReadOnlyList<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>? EndpointFilterFactories { get; init; }
-    public IList<object>? EndpointMetadata { get; init; }
+    public EndpointBuilder? EndpointBuilder { get; init; }
}
namespace Microsoft.AspNetCore.Http;

public sealed class EndpointFilterFactoryContext
{
-    public EndpointFilterFactoryContext(MethodInfo methodInfo, IList<object> endpointMetadata, IServiceProvider applicationServices);
+    public EndpointFilterFactoryContext(MethodInfo methodInfo, EndpointBuilder endopintBuilder);

-    public IList<object> EndpointMetadata { get; }
-    public IServiceProvider ApplicationServices { get; }
+    public EndpointBuilder EndpointBuilder { get; }
}

Usage Examples

Before:

RequestDelegateFactoryOptions factoryOptions = new()
{
    ServiceProvider = _applicationServices,
    RouteParameterNames = routeParamNames,
    ThrowOnBadRequest = _throwOnBadRequest,
    DisableInferBodyFromParameters = ShouldDisableInferredBodyParameters(entry.HttpMethods),
    EndpointMetadata = builder.Metadata,
    // builder.EndopintFilterFactories is not settable so is always a List.
    EndpointFilterFactories = (IReadOnlyList<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>)builder.EndpointFilterFactories,
};

After:

RequestDelegateFactoryOptions factoryOptions = new()
{
    ServiceProvider = _applicationServices,
    RouteParameterNames = routeParamNames,
    ThrowOnBadRequest = _throwOnBadRequest,
    DisableInferBodyFromParameters = ShouldDisableInferredBodyParameters(entry.HttpMethods),
    EndpointBuilder = builder,
};

Alternative Designs

We could keep the filter factories directly on RequestDelegateFactoryOptions but rename EndpointFilterFactories to FilterFactories to match the name in EndpointBuilder and make it an IList instead of IReadOnlyList which aligns with RequestDelegateFactoryOptions.Metadata.

namespace Microsoft.AspNetCore.Http;

public sealed class RequestDelegateFactoryOptions
{
-    public IReadOnlyList<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>? EndpointFilterFactories { get; init; }
+    public IList<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>? FilterFactories { get; init; }
}

We could also keep it as an IReadOnlyList. Arguably this communicates better that the RequestDelegateFactory won't mutate it. RDF does mutate EndpointMetadata which is an IList so that might add to the confusion.

Risks

Low. This affects API that has only been released in previews and few people depend on.

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedold-area-web-frameworks-do-not-use*DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions