diff --git a/README.md b/README.md index e42698fc..0c90f78b 100644 --- a/README.md +++ b/README.md @@ -894,6 +894,10 @@ are long-lived, using scoped services within a user context builder will result scoped services having a matching long lifetime. You may wish to alleviate this by creating a service scope temporarily within your user context builder. +For applications that service multiple schemas, you may register `IUserContextBuilder` +to create a user context for a specific schema. This is useful when you need to create +a user context that is specific to a particular schema. + ### Mutations within GET requests For security reasons and pursuant to current recommendations, mutation GraphQL requests diff --git a/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs b/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs index 3aceec51..adf2a4cf 100644 --- a/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs +++ b/src/Transports.AspNetCore/GraphQLHttpMiddleware.cs @@ -36,6 +36,15 @@ public GraphQLHttpMiddleware( : base(next, serializer, documentExecuter, serviceScopeFactory, options, hostApplicationLifetime) { } + + /// + protected override ValueTask?> BuildUserContextAsync(HttpContext context, object? payload) + { + var userContextBuilder = context.RequestServices.GetService>(); + return userContextBuilder == null + ? base.BuildUserContextAsync(context, payload) + : userContextBuilder.BuildUserContextAsync(context, payload); + } } /// @@ -677,17 +686,18 @@ protected virtual async Task ExecuteRequestAsync(HttpContext co /// via /// if needed. ///

- /// By default this method pulls the registered , - /// if any, within the service scope and executes it to build the user context. + /// By default this method pulls the registered + /// or instance, if any, within the service scope + /// and executes it to build the user context. /// In this manner, both scoped and singleton /// instances are supported, although singleton instances are recommended. ///
- protected virtual async ValueTask?> BuildUserContextAsync(HttpContext context, object? payload) + protected virtual ValueTask?> BuildUserContextAsync(HttpContext context, object? payload) { var userContextBuilder = context.RequestServices.GetService(); return userContextBuilder == null - ? null - : await userContextBuilder.BuildUserContextAsync(context, payload); + ? default // successful result of null + : userContextBuilder.BuildUserContextAsync(context, payload); } ValueTask?> IUserContextBuilder.BuildUserContextAsync(HttpContext context, object? payload) diff --git a/src/Transports.AspNetCore/IUserContextBuilder.cs b/src/Transports.AspNetCore/IUserContextBuilder.cs index c6764478..d88682d1 100644 --- a/src/Transports.AspNetCore/IUserContextBuilder.cs +++ b/src/Transports.AspNetCore/IUserContextBuilder.cs @@ -24,3 +24,9 @@ public interface IUserContextBuilder /// Dictionary object representing user context. Return to use default user context. ValueTask?> BuildUserContextAsync(HttpContext context, object? payload); } + +/// +public interface IUserContextBuilder : IUserContextBuilder + where TSchema : ISchema +{ +} diff --git a/tests/ApiApprovalTests/net50+net60+netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt b/tests/ApiApprovalTests/net50+net60+netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt index 2686b29a..4fdc10f2 100644 --- a/tests/ApiApprovalTests/net50+net60+netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt +++ b/tests/ApiApprovalTests/net50+net60+netcoreapp31/GraphQL.Server.Transports.AspNetCore.approved.txt @@ -145,6 +145,7 @@ namespace GraphQL.Server.Transports.AspNetCore where TSchema : GraphQL.Types.ISchema { public GraphQLHttpMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, GraphQL.IGraphQLTextSerializer serializer, GraphQL.IDocumentExecuter documentExecuter, Microsoft.Extensions.DependencyInjection.IServiceScopeFactory serviceScopeFactory, GraphQL.Server.Transports.AspNetCore.GraphQLHttpMiddlewareOptions options, Microsoft.Extensions.Hosting.IHostApplicationLifetime hostApplicationLifetime) { } + protected override System.Threading.Tasks.ValueTask?> BuildUserContextAsync(Microsoft.AspNetCore.Http.HttpContext context, object? payload) { } } public sealed class HttpGetValidationRule : GraphQL.Validation.IValidationRule { @@ -166,6 +167,8 @@ namespace GraphQL.Server.Transports.AspNetCore { System.Threading.Tasks.ValueTask?> BuildUserContextAsync(Microsoft.AspNetCore.Http.HttpContext context, object? payload); } + public interface IUserContextBuilder : GraphQL.Server.Transports.AspNetCore.IUserContextBuilder + where TSchema : GraphQL.Types.ISchema { } public class UserContextBuilder : GraphQL.Server.Transports.AspNetCore.IUserContextBuilder where TUserContext : System.Collections.Generic.IDictionary { diff --git a/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt b/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt index 6fd19749..d2d58630 100644 --- a/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt +++ b/tests/ApiApprovalTests/netcoreapp21+netstandard20/GraphQL.Server.Transports.AspNetCore.approved.txt @@ -152,6 +152,7 @@ namespace GraphQL.Server.Transports.AspNetCore where TSchema : GraphQL.Types.ISchema { public GraphQLHttpMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, GraphQL.IGraphQLTextSerializer serializer, GraphQL.IDocumentExecuter documentExecuter, Microsoft.Extensions.DependencyInjection.IServiceScopeFactory serviceScopeFactory, GraphQL.Server.Transports.AspNetCore.GraphQLHttpMiddlewareOptions options, GraphQL.Server.Transports.AspNetCore.IHostApplicationLifetime hostApplicationLifetime) { } + protected override System.Threading.Tasks.ValueTask?> BuildUserContextAsync(Microsoft.AspNetCore.Http.HttpContext context, object? payload) { } } public class HostApplicationLifetime : GraphQL.Server.Transports.AspNetCore.IHostApplicationLifetime, Microsoft.Extensions.Hosting.IHostedService { @@ -184,6 +185,8 @@ namespace GraphQL.Server.Transports.AspNetCore { System.Threading.Tasks.ValueTask?> BuildUserContextAsync(Microsoft.AspNetCore.Http.HttpContext context, object? payload); } + public interface IUserContextBuilder : GraphQL.Server.Transports.AspNetCore.IUserContextBuilder + where TSchema : GraphQL.Types.ISchema { } public class UserContextBuilder : GraphQL.Server.Transports.AspNetCore.IUserContextBuilder where TUserContext : System.Collections.Generic.IDictionary {