Skip to content

Commit cacb72f

Browse files
authored
Introduce GraphQLHttpMiddleware.HandleRequest and switch to IMiddleware (#735)
1 parent 061c0a7 commit cacb72f

File tree

11 files changed

+95
-24
lines changed

11 files changed

+95
-24
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ public void ConfigureServices(IServiceCollection services)
129129
// Add GraphQL services and configure options
130130
services.AddGraphQL(builder => builder
131131
.AddServer(true)
132+
.AddHttpMiddleware<ChatSchema>()
133+
.AddWebSocketsHttpMiddleware<ChatSchema>()
132134
// For subscriptions support
133135
.AddDocumentExecuter<SubscriptionDocumentExecuter>()
134136
.AddSchema<ChatSchema>()
@@ -189,6 +191,8 @@ public void ConfigureServices(IServiceCollection services)
189191
// Add GraphQL services and configure options
190192
services.AddGraphQL(builder => builder
191193
.AddServer(true)
194+
.AddHttpMiddleware<ChatSchema>()
195+
.AddWebSocketsHttpMiddleware<ChatSchema>()
192196
// For subscriptions support
193197
.AddDocumentExecuter<SubscriptionDocumentExecuter>()
194198
.AddSchema<ChatSchema>()

samples/Samples.Server/GraphQLHttpMiddlewareWithLogs.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ public class GraphQLHttpMiddlewareWithLogs<TSchema> : GraphQLHttpMiddleware<TSch
1616

1717
public GraphQLHttpMiddlewareWithLogs(
1818
ILogger<GraphQLHttpMiddleware<TSchema>> logger,
19-
RequestDelegate next,
2019
IGraphQLTextSerializer requestDeserializer)
21-
: base(next, requestDeserializer)
20+
: base(requestDeserializer)
2221
{
2322
_logger = logger;
2423
}

samples/Samples.Server/Startup.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public void ConfigureServices(IServiceCollection services)
4343

4444
services.AddGraphQL(builder => builder
4545
.AddServer(true)
46+
.AddHttpMiddleware<ChatSchema, GraphQLHttpMiddlewareWithLogs<ChatSchema>>()
47+
.AddWebSocketsHttpMiddleware<ChatSchema>()
4648
.AddSubscriptionExecutionStrategy()
4749
.AddSchema<ChatSchema>()
4850
.ConfigureExecutionOptions(options =>

samples/Samples.Server/StartupWithRouting.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public void ConfigureServices(IServiceCollection services)
4242

4343
services.AddGraphQL(builder => builder
4444
.AddServer(true)
45+
.AddHttpMiddleware<ChatSchema, GraphQLHttpMiddlewareWithLogs<ChatSchema>>()
46+
.AddWebSocketsHttpMiddleware<ChatSchema>()
4547
.AddSubscriptionExecutionStrategy()
4648
.AddSchema<ChatSchema>()
4749
.ConfigureExecutionOptions(options =>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#nullable enable
2+
3+
using GraphQL.DI;
4+
using GraphQL.Server.Transports.AspNetCore;
5+
using GraphQL.Types;
6+
7+
namespace GraphQL.Server
8+
{
9+
/// <summary>
10+
/// GraphQL specific extension methods for <see cref="IGraphQLBuilder"/>.
11+
/// </summary>
12+
public static class GraphQLBuilderMiddlewareExtensions
13+
{
14+
public static IGraphQLBuilder AddHttpMiddleware<TSchema>(this IGraphQLBuilder builder)
15+
where TSchema : ISchema
16+
{
17+
builder.Services.Register<GraphQLHttpMiddleware<TSchema>, GraphQLHttpMiddleware<TSchema>>(ServiceLifetime.Singleton);
18+
return builder;
19+
}
20+
21+
public static IGraphQLBuilder AddHttpMiddleware<TSchema, TMiddleware>(this IGraphQLBuilder builder)
22+
where TSchema : ISchema
23+
where TMiddleware : GraphQLHttpMiddleware<TSchema>
24+
{
25+
builder.Services.Register<TMiddleware, TMiddleware>(ServiceLifetime.Singleton);
26+
return builder;
27+
}
28+
}
29+
}

src/Transports.AspNetCore/GraphQLHttpMiddleware.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,23 @@ namespace GraphQL.Server.Transports.AspNetCore
2626
/// Attention! The current implementation does not impose such a restriction and allows mutations in GET requests.
2727
/// </summary>
2828
/// <typeparam name="TSchema">Type of GraphQL schema that is used to validate and process requests.</typeparam>
29-
public class GraphQLHttpMiddleware<TSchema>
29+
public class GraphQLHttpMiddleware<TSchema> : IMiddleware
3030
where TSchema : ISchema
3131
{
3232
private const string DOCS_URL = "See: http://graphql.org/learn/serving-over-http/.";
3333

34-
private readonly RequestDelegate _next;
3534
private readonly IGraphQLTextSerializer _serializer;
3635

37-
public GraphQLHttpMiddleware(RequestDelegate next, IGraphQLTextSerializer serializer)
36+
public GraphQLHttpMiddleware(IGraphQLTextSerializer serializer)
3837
{
39-
_next = next;
4038
_serializer = serializer;
4139
}
4240

43-
public async Task InvokeAsync(HttpContext context)
41+
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
4442
{
4543
if (context.WebSockets.IsWebSocketRequest)
4644
{
47-
await _next(context);
45+
await next(context);
4846
return;
4947
}
5048

@@ -53,7 +51,6 @@ public async Task InvokeAsync(HttpContext context)
5351
var httpRequest = context.Request;
5452
var httpResponse = context.Response;
5553

56-
var serializer = context.RequestServices.GetRequiredService<IGraphQLSerializer>();
5754
var cancellationToken = GetCancellationToken(context);
5855

5956
// GraphQL HTTP only supports GET and POST methods
@@ -149,7 +146,17 @@ public async Task InvokeAsync(HttpContext context)
149146
: await userContextBuilder.BuildUserContext(context);
150147

151148
var executer = context.RequestServices.GetRequiredService<IGraphQLExecuter<TSchema>>();
149+
await HandleRequestAsync(context, userContext, bodyGQLBatchRequest, gqlRequest, executer, cancellationToken);
150+
}
152151

152+
protected virtual async Task HandleRequestAsync(
153+
HttpContext context,
154+
IDictionary<string, object> userContext,
155+
GraphQLRequest[] bodyGQLBatchRequest,
156+
GraphQLRequest gqlRequest,
157+
IGraphQLExecuter<TSchema> executer,
158+
CancellationToken cancellationToken)
159+
{
153160
// Normal execution with single graphql request
154161
if (bodyGQLBatchRequest == null)
155162
{
@@ -159,7 +166,7 @@ public async Task InvokeAsync(HttpContext context)
159166

160167
await RequestExecutedAsync(new GraphQLRequestExecutionResult(gqlRequest, result, stopwatch.Elapsed));
161168

162-
await WriteResponseAsync(httpResponse, serializer, cancellationToken, result);
169+
await WriteResponseAsync(context.Response, _serializer, cancellationToken, result);
163170
}
164171
// Execute multiple graphql requests in one batch
165172
else
@@ -178,7 +185,7 @@ public async Task InvokeAsync(HttpContext context)
178185
executionResults[i] = result;
179186
}
180187

181-
await WriteResponseAsync(httpResponse, serializer, cancellationToken, executionResults);
188+
await WriteResponseAsync(context.Response, _serializer, cancellationToken, executionResults);
182189
}
183190
}
184191

src/Transports.Subscriptions.WebSockets/Extensions/GraphQLBuilderWebSocketsExtensions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using GraphQL.DI;
22
using GraphQL.Server.Transports.Subscriptions.Abstractions;
33
using GraphQL.Server.Transports.WebSockets;
4+
using GraphQL.Types;
45

56
namespace GraphQL.Server
67
{
@@ -18,5 +19,20 @@ public static IGraphQLBuilder AddWebSockets(this IGraphQLBuilder builder)
1819

1920
return builder;
2021
}
22+
23+
public static IGraphQLBuilder AddWebSocketsHttpMiddleware<TSchema>(this IGraphQLBuilder builder)
24+
where TSchema : ISchema
25+
{
26+
builder.Services.Register<GraphQLWebSocketsMiddleware<TSchema>, GraphQLWebSocketsMiddleware<TSchema>>(ServiceLifetime.Singleton);
27+
return builder;
28+
}
29+
30+
public static IGraphQLBuilder AddWebSocketsHttpMiddleware<TSchema, TMiddleware>(this IGraphQLBuilder builder)
31+
where TSchema : ISchema
32+
where TMiddleware : GraphQLWebSocketsMiddleware<TSchema>
33+
{
34+
builder.Services.Register<TMiddleware, TMiddleware>(ServiceLifetime.Singleton);
35+
return builder;
36+
}
2137
}
2238
}

src/Transports.Subscriptions.WebSockets/GraphQLWebSocketsMiddleware.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,17 @@ namespace GraphQL.Server.Transports.WebSockets
1212
/// ASP.NET Core middleware for processing GraphQL web socket requests. This middleware useful with and without ASP.NET Core routing.
1313
/// </summary>
1414
/// <typeparam name="TSchema">Type of GraphQL schema that is used to process requests.</typeparam>
15-
public class GraphQLWebSocketsMiddleware<TSchema>
15+
public class GraphQLWebSocketsMiddleware<TSchema> : IMiddleware
1616
where TSchema : ISchema
1717
{
18-
private readonly RequestDelegate _next;
1918
private readonly ILogger<GraphQLWebSocketsMiddleware<TSchema>> _logger;
2019

21-
public GraphQLWebSocketsMiddleware(RequestDelegate next, ILogger<GraphQLWebSocketsMiddleware<TSchema>> logger)
20+
public GraphQLWebSocketsMiddleware(ILogger<GraphQLWebSocketsMiddleware<TSchema>> logger)
2221
{
23-
_next = next;
2422
_logger = logger;
2523
}
2624

27-
public async Task InvokeAsync(HttpContext context)
25+
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
2826
{
2927
using (_logger.BeginScope(new Dictionary<string, object>
3028
{
@@ -35,7 +33,7 @@ public async Task InvokeAsync(HttpContext context)
3533
if (!context.WebSockets.IsWebSocketRequest)
3634
{
3735
_logger.LogDebug("Request is not a valid websocket request");
38-
await _next(context);
36+
await next(context);
3937

4038
return;
4139
}

tests/ApiApprovalTests/GraphQL.Server.Transports.AspNetCore.approved.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
namespace GraphQL.Server
22
{
3+
public static class GraphQLBuilderMiddlewareExtensions
4+
{
5+
public static GraphQL.DI.IGraphQLBuilder AddHttpMiddleware<TSchema>(this GraphQL.DI.IGraphQLBuilder builder)
6+
where TSchema : GraphQL.Types.ISchema { }
7+
public static GraphQL.DI.IGraphQLBuilder AddHttpMiddleware<TSchema, TMiddleware>(this GraphQL.DI.IGraphQLBuilder builder)
8+
where TSchema : GraphQL.Types.ISchema
9+
where TMiddleware : GraphQL.Server.Transports.AspNetCore.GraphQLHttpMiddleware<TSchema> { }
10+
}
311
public static class GraphQLBuilderUserContextExtensions
412
{
513
public static GraphQL.DI.IGraphQLBuilder AddDefaultEndpointSelectorPolicy(this GraphQL.DI.IGraphQLBuilder builder) { }
@@ -13,17 +21,18 @@ namespace GraphQL.Server
1321
}
1422
namespace GraphQL.Server.Transports.AspNetCore
1523
{
16-
public class GraphQLHttpMiddleware<TSchema>
24+
public class GraphQLHttpMiddleware<TSchema> : Microsoft.AspNetCore.Http.IMiddleware
1725
where TSchema : GraphQL.Types.ISchema
1826
{
19-
public GraphQLHttpMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, GraphQL.IGraphQLTextSerializer serializer) { }
27+
public GraphQLHttpMiddleware(GraphQL.IGraphQLTextSerializer serializer) { }
2028
protected virtual System.Threading.CancellationToken GetCancellationToken(Microsoft.AspNetCore.Http.HttpContext context) { }
2129
protected virtual System.Threading.Tasks.Task HandleContentTypeCouldNotBeParsedErrorAsync(Microsoft.AspNetCore.Http.HttpContext context) { }
2230
protected virtual System.Threading.Tasks.ValueTask<bool> HandleDeserializationErrorAsync(Microsoft.AspNetCore.Http.HttpContext context, System.Exception ex) { }
2331
protected virtual System.Threading.Tasks.Task HandleInvalidContentTypeErrorAsync(Microsoft.AspNetCore.Http.HttpContext context) { }
2432
protected virtual System.Threading.Tasks.Task HandleInvalidHttpMethodErrorAsync(Microsoft.AspNetCore.Http.HttpContext context) { }
2533
protected virtual System.Threading.Tasks.Task HandleNoQueryErrorAsync(Microsoft.AspNetCore.Http.HttpContext context) { }
26-
public System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context) { }
34+
protected virtual System.Threading.Tasks.Task HandleRequestAsync(Microsoft.AspNetCore.Http.HttpContext context, System.Collections.Generic.IDictionary<string, object> userContext, GraphQL.Transport.GraphQLRequest[] bodyGQLBatchRequest, GraphQL.Transport.GraphQLRequest gqlRequest, GraphQL.Server.IGraphQLExecuter<TSchema> executer, System.Threading.CancellationToken cancellationToken) { }
35+
public System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Http.RequestDelegate next) { }
2736
protected virtual System.Threading.Tasks.Task RequestExecutedAsync(in GraphQL.Server.Transports.AspNetCore.GraphQLRequestExecutionResult requestExecutionResult) { }
2837
protected virtual System.Threading.Tasks.Task RequestExecutingAsync(GraphQL.Transport.GraphQLRequest request, int? indexInBatch = default) { }
2938
protected virtual System.Threading.Tasks.Task WriteErrorResponseAsync(Microsoft.AspNetCore.Http.HttpContext context, string errorMessage, System.Net.HttpStatusCode httpStatusCode) { }

tests/ApiApprovalTests/GraphQL.Server.Transports.Subscriptions.WebSockets.approved.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@ namespace GraphQL.Server
33
public static class GraphQLBuilderWebSocketsExtensions
44
{
55
public static GraphQL.DI.IGraphQLBuilder AddWebSockets(this GraphQL.DI.IGraphQLBuilder builder) { }
6+
public static GraphQL.DI.IGraphQLBuilder AddWebSocketsHttpMiddleware<TSchema>(this GraphQL.DI.IGraphQLBuilder builder)
7+
where TSchema : GraphQL.Types.ISchema { }
8+
public static GraphQL.DI.IGraphQLBuilder AddWebSocketsHttpMiddleware<TSchema, TMiddleware>(this GraphQL.DI.IGraphQLBuilder builder)
9+
where TSchema : GraphQL.Types.ISchema
10+
where TMiddleware : GraphQL.Server.Transports.WebSockets.GraphQLWebSocketsMiddleware<TSchema> { }
611
}
712
}
813
namespace GraphQL.Server.Transports.WebSockets
914
{
10-
public class GraphQLWebSocketsMiddleware<TSchema>
15+
public class GraphQLWebSocketsMiddleware<TSchema> : Microsoft.AspNetCore.Http.IMiddleware
1116
where TSchema : GraphQL.Types.ISchema
1217
{
13-
public GraphQLWebSocketsMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.Extensions.Logging.ILogger<GraphQL.Server.Transports.WebSockets.GraphQLWebSocketsMiddleware<TSchema>> logger) { }
14-
public System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context) { }
18+
public GraphQLWebSocketsMiddleware(Microsoft.Extensions.Logging.ILogger<GraphQL.Server.Transports.WebSockets.GraphQLWebSocketsMiddleware<TSchema>> logger) { }
19+
public System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Http.RequestDelegate next) { }
1520
}
1621
public interface IWebSocketConnectionFactory<TSchema>
1722
where TSchema : GraphQL.Types.ISchema

0 commit comments

Comments
 (0)