Skip to content

Add access to services for route handler filter factories to RouteHandlerContext #41900

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
DamianEdwards opened this issue May 27, 2022 · 9 comments · Fixed by #41952
Closed
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-minimal-actions Controller-like actions for endpoint routing old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels
Milestone

Comments

@DamianEdwards
Copy link
Member

DamianEdwards commented May 27, 2022

When adding a route handler filter via a filter factory there are cases where the filter factory (as opposed to the filter itself) needs access to the application services, e.g. to get an ILogger instance. We should add a property to RouteHandlerContext to enable this.

namespace Microsoft.AspNetCore.Http;

public sealed class RouteHandlerContext
{
+    public IServiceProvider ApplicationServices { get; }
}

Note that the property is nullable due to where it's instantiated in RequestDelegateFactory and matches the Services properties on EndpointMetadataContext and EndpointParameterMetadataContext.

Example Usage

public static RouteHandlerBuilder WithParameterValidation(this RouteHandlerBuilder builder, int statusCode = StatusCodes.Status400BadRequest)
{
    builder.Add(eb =>
    {
        var methodInfo = eb.Metadata.OfType<MethodInfo>().SingleOrDefault();
        if (methodInfo is not null && IsValidatable(methodInfo))
        {
            eb.Metadata.Add(new ProducesResponseTypeAttribute(typeof(HttpValidationProblemDetails), statusCode, "application/problem+json"));
        }
    });
    builder.AddFilter((RouteHandlerContext context, RouteHandlerFilterDelegate next) =>
    {
        // *************************************
        // ** We use ApplicationServices here **
        // *************************************
        var loggerFactory = context.ApplicationServices.GetRequiredService<ILoggerFactory>();
        var logger = loggerFactory.Create("MinimalApis.Extensions.Filters.ValidationRouteHandlerFilterFactory");
 
        if (!IsValidatable(context.MethodInfo))
        {
            return next;
        }
 
        return rhic =>
        {
            foreach (var parameter in rhic.Parameters)
            {
                if (parameter is not null && !MiniValidator.TryValidate(parameter, out var errors))
                {
                    return new(Results.ValidationProblem(errors));
                }
            }
 
            return next(rhic);
        };
    });

    return builder;
}

Risks

Should be very low. This is following the pattern used in other context classes including those where the call site originates in RequestDelegateFactory, e.g. EndpointMetadataContext.

@DamianEdwards DamianEdwards added api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews feature-minimal-actions Controller-like actions for endpoint routing old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels labels May 27, 2022
@ghost
Copy link

ghost commented May 27, 2022

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

DamianEdwards added a commit that referenced this issue May 31, 2022
- Added `public IServiceProvider? Services { get; }` property to `RouteHandlerContext`
- Enables route handler filter factories to access app services
- Added a unit test & updated another to ensure app's service provider is passed through correctly by `RequestDelegateFactory`

Fixes #41900
@DamianEdwards DamianEdwards self-assigned this May 31, 2022
@DamianEdwards DamianEdwards added this to the 7.0-preview6 milestone May 31, 2022
@halter73
Copy link
Member

In IApplicationBuilder, IConnectionBuilder and KestrelServerOptions we call this ApplicationServices to make it extra clear it's different from HttpContext.RequestServices. Should we do the same here?

@davidfowl
Copy link
Member

Yep.

@halter73
Copy link
Member

halter73 commented Jun 1, 2022

From our email thread,

This is following the pattern used in other context classes including those where the call site originates in RequestDelegateFactory, e.g. EndpointMetadataContext

EndpointMetadataContext.Services hasn't shipped in a major release yet. Should we make it EndpointMetadataContext.ApplicationServices?

namespace Microsoft.AspNetCore.Http;

public sealed class RouteHandlerContext
{
+    public IServiceProvider? ApplicationServices { get; }
}

namespace Microsoft.AspNetCore.Http.Metadata;

public sealed class EndpointMetadataContext
{
-    public IServiceProvider? Services { get; }
+    public IServiceProvider? ApplicationServices { get; }
}

@DamianEdwards
Copy link
Member Author

EndpointMetadataContext.Services hasn't shipped in a major release yet. Should we make it EndpointMetadataContext.ApplicationServices?

Sounds fair to me.

@halter73
Copy link
Member

halter73 commented Jun 2, 2022

From @davidfowl's email:

I don't think the property should be nullable.

That's a good point. At first, I thought you were suggesting we lie about the nullability, but it occurs to me that we can just provide an "empty" IServiceProvider if it would otherwise be null. We should do the same for the EndpointMetadataContext.

@DamianEdwards
Copy link
Member Author

Agreed. Fixed in the PR.

@halter73
Copy link
Member

halter73 commented Jun 3, 2022

Updated API proposal:

namespace Microsoft.AspNetCore.Http;

public sealed class RouteHandlerContext
{
-     public RouteHandlerContext(MethodInfo methodInfo, EndpointMetadataCollection endpointMetadata);
+     public RouteHandlerContext(MethodInfo methodInfo, EndpointMetadataCollection endpointMetadata, IServiceProvider applicationServices);

+     public IServiceProvider ApplicationServices { get; }
}

namespace Microsoft.AspNetCore.Http.Metadata;

public sealed class EndpointMetadataContext
{
-     public EndpointMetadataContext(MethodInfo method, IList<object> endpointMetadata, IServiceProvider? services);
+     public EndpointMetadataContext(MethodInfo method, IList<object> endpointMetadata, IServiceProvider applicationServices);

-     public IServiceProvider? Services { get; }
+     public IServiceProvider ApplicationServices { get; }
}

public sealed class EndpointParameterMetadataContext
{
-     public EndpointParameterMetadataContext(ParameterInfo parameter, IList<object> endpointMetadata, IServiceProvider? services);
+     public EndpointParameterMetadataContext(ParameterInfo parameter, IList<object> endpointMetadata, IServiceProvider applicationServices);

-     public IServiceProvider? Services { get; }
+     public IServiceProvider ApplicationServices { get; }
}

@halter73
Copy link
Member

halter73 commented Jun 6, 2022

API Review Note:

  • No further feedback. Approved!

@halter73 halter73 added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Jun 6, 2022
DamianEdwards added a commit that referenced this issue Jun 6, 2022
…41952)

- Added `public IServiceProvider ApplicationServices { get; }` property to `RouteHandlerContext`
  - Enables route handler filter factories to access app services
- Renamed existing `Services` property on `EndpointParameterMetadataContext` and `EndpointMetadataContext` to `ApplicationServices` and made it non-nullable
- Added a unit test & updated another to ensure app's service provider is passed through correctly by `RequestDelegateFactory`

Fixes #41900
captainsafia pushed a commit to captainsafia/aspnetcore that referenced this issue Jun 13, 2022
…otnet#41952)

- Added `public IServiceProvider ApplicationServices { get; }` property to `RouteHandlerContext`
  - Enables route handler filter factories to access app services
- Renamed existing `Services` property on `EndpointParameterMetadataContext` and `EndpointMetadataContext` to `ApplicationServices` and made it non-nullable
- Added a unit test & updated another to ensure app's service provider is passed through correctly by `RequestDelegateFactory`

Fixes dotnet#41900
@ghost ghost locked as resolved and limited conversation to collaborators Jul 6, 2022
@amcasey amcasey added the area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc label Jun 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-minimal-actions Controller-like actions for endpoint routing old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants