Skip to content

Commit 6bba2cb

Browse files
authored
Support APQ middleware; return BadRequest for any validation error (#791)
1 parent dfebec5 commit 6bba2cb

File tree

10 files changed

+48
-30
lines changed

10 files changed

+48
-30
lines changed

Directory.Build.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
<AssemblyName>GraphQL.Server.$(MSBuildProjectName)</AssemblyName>
2727
<RootNamespace>GraphQL.Server.$(MSBuildProjectName)</RootNamespace>
2828
<PackageId>GraphQL.Server.$(MSBuildProjectName)</PackageId>
29+
30+
<GraphQLVersion>5.3.0</GraphQLVersion>
2931
</PropertyGroup>
3032

3133
<ItemGroup Condition="'$(IsPackable)' == 'true'">

samples/Samples.Server/Samples.Server.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="GraphQL.DataLoader" Version="5.1.1" />
12+
<PackageReference Include="GraphQL.DataLoader" Version="$(GraphQLVersion)" />
1313
<PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
1414
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
15-
<PackageReference Include="GraphQL.MicrosoftDI" Version="5.1.1" />
16-
<PackageReference Include="GraphQL.SystemTextJson" Version="5.1.1" />
15+
<PackageReference Include="GraphQL.MicrosoftDI" Version="$(GraphQLVersion)" />
16+
<PackageReference Include="GraphQL.SystemTextJson" Version="$(GraphQLVersion)" />
1717
</ItemGroup>
1818

1919
<ItemGroup>

samples/Samples.Server/Startup.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using GraphQL.DataLoader;
22
using GraphQL.Execution;
3-
using GraphQL.Instrumentation;
43
using GraphQL.MicrosoftDI;
54
using GraphQL.Samples.Schemas.Chat;
65
using GraphQL.Server;
@@ -34,8 +33,7 @@ public void ConfigureServices(IServiceCollection services)
3433
.AddTransient<IAuthorizationErrorMessageBuilder, DefaultAuthorizationErrorMessageBuilder>(); // required by CustomErrorInfoProvider
3534

3635
services.AddGraphQL(builder => builder
37-
.AddMetrics()
38-
.AddDocumentExecuter<ApolloTracingDocumentExecuter>()
36+
.AddApolloTracing()
3937
.AddHttpMiddleware<ChatSchema, GraphQLHttpMiddlewareWithLogs<ChatSchema>>()
4038
.AddWebSocketsHttpMiddleware<ChatSchema>()
4139
.AddSchema<ChatSchema>()

samples/Samples.Server/StartupWithRouting.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using GraphQL.DataLoader;
22
using GraphQL.Execution;
3-
using GraphQL.Instrumentation;
43
using GraphQL.MicrosoftDI;
54
using GraphQL.Samples.Schemas.Chat;
65
using GraphQL.Server;
@@ -35,8 +34,7 @@ public void ConfigureServices(IServiceCollection services)
3534
.AddTransient<IAuthorizationErrorMessageBuilder, DefaultAuthorizationErrorMessageBuilder>(); // required by CustomErrorInfoProvider
3635

3736
services.AddGraphQL(builder => builder
38-
.AddMetrics()
39-
.AddDocumentExecuter<ApolloTracingDocumentExecuter>()
37+
.AddApolloTracing()
4038
.AddHttpMiddleware<ChatSchema, GraphQLHttpMiddlewareWithLogs<ChatSchema>>()
4139
.AddWebSocketsHttpMiddleware<ChatSchema>()
4240
.AddSchema<ChatSchema>()

src/All/All.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
<ProjectReference Include="..\Ui.Playground\Ui.Playground.csproj" />
1919
<ProjectReference Include="..\Ui.Voyager\Ui.Voyager.csproj" />
2020

21-
<PackageReference Include="GraphQL.MemoryCache" Version="5.1.1" />
22-
<PackageReference Include="GraphQL.MicrosoftDI" Version="5.1.1" />
23-
<PackageReference Include="GraphQL.SystemTextJson" Version="5.1.1" />
21+
<PackageReference Include="GraphQL.MemoryCache" Version="$(GraphQLVersion)" />
22+
<PackageReference Include="GraphQL.MicrosoftDI" Version="$(GraphQLVersion)" />
23+
<PackageReference Include="GraphQL.SystemTextJson" Version="$(GraphQLVersion)" />
2424
</ItemGroup>
2525

2626
</Project>

src/Authorization.AspNetCore/Authorization.AspNetCore.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<ItemGroup>
1010
<FrameworkReference Include="Microsoft.AspNetCore.App" />
11-
<PackageReference Include="GraphQL" Version="5.1.1" />
11+
<PackageReference Include="GraphQL" Version="$(GraphQLVersion)" />
1212
</ItemGroup>
1313

1414
</Project>

src/Transports.AspNetCore/GraphQLHttpMiddleware.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,6 @@ public virtual async Task InvokeAsync(HttpContext context, RequestDelegate next)
157157
Extensions = urlGQLRequest.Extensions ?? bodyGQLRequest?.Extensions,
158158
OperationName = urlGQLRequest.OperationName ?? bodyGQLRequest?.OperationName
159159
};
160-
161-
if (string.IsNullOrWhiteSpace(gqlRequest.Query))
162-
{
163-
await HandleNoQueryErrorAsync(context);
164-
return;
165-
}
166160
}
167161

168162
// Prepare context and execute
@@ -284,7 +278,7 @@ protected virtual Task WriteErrorResponseAsync(HttpContext context, string error
284278
protected virtual Task WriteResponseAsync<TResult>(HttpResponse httpResponse, IGraphQLSerializer serializer, CancellationToken cancellationToken, TResult result)
285279
{
286280
httpResponse.ContentType = "application/json";
287-
httpResponse.StatusCode = 200; // OK
281+
httpResponse.StatusCode = result is not ExecutionResult executionResult || executionResult.Executed ? 200 : 400; // BadRequest when fails validation; OK otherwise
288282

289283
return serializer.WriteAsync(httpResponse.Body, result, cancellationToken);
290284
}

src/Transports.AspNetCore/Transports.AspNetCore.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<ItemGroup>
1010
<FrameworkReference Include="Microsoft.AspNetCore.App" />
11-
<PackageReference Include="GraphQL" Version="5.1.1" />
11+
<PackageReference Include="GraphQL" Version="$(GraphQLVersion)" />
1212
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.0" Condition="'$(TargetFramework)' == 'netcoreapp3.1'" />
1313
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" Condition="'$(TargetFramework)' == 'net5'" />
1414
</ItemGroup>

src/Transports.Subscriptions.Abstractions/Transports.Subscriptions.Abstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="GraphQL" Version="5.1.1" />
9+
<PackageReference Include="GraphQL" Version="$(GraphQLVersion)" />
1010
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.0" />
1111
<PackageReference Include="System.Reactive" Version="5.0.0" />
1212
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="5.0.0" />

tests/Samples.Server.Tests/ResponseTests.cs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,9 @@ public async Task Batched_Query_Should_Return_Single_Result_As_Array()
7171
[Theory]
7272
[MemberData(nameof(WrongQueryData))]
7373
public async Task Wrong_Query_Should_Return_Error(HttpMethod httpMethod, HttpContent httpContent,
74-
HttpStatusCode expectedStatusCode, string expectedErrorMsg)
74+
HttpStatusCode expectedStatusCode, string expected)
7575
{
7676
var response = await SendRequestAsync(httpMethod, httpContent);
77-
string expected = @"{""errors"":[{""message"":""" + expectedErrorMsg + @"""}]}";
7877

7978
response.StatusCode.ShouldBe(expectedStatusCode);
8079

@@ -90,7 +89,7 @@ public async Task Wrong_Query_Should_Return_Error(HttpMethod httpMethod, HttpCon
9089
HttpMethod.Put,
9190
new StringContent(Serializer.ToJson(new GraphQLRequest { Query = "query { __schema { queryType { name } } }" }), Encoding.UTF8, "application/json"),
9291
HttpStatusCode.MethodNotAllowed,
93-
"Invalid HTTP method. Only GET and POST are supported. See: http://graphql.org/learn/serving-over-http/.",
92+
@"{""errors"":[{""message"":""Invalid HTTP method. Only GET and POST are supported. See: http://graphql.org/learn/serving-over-http/.""}]}",
9493
},
9594

9695
// POST with unsupported mime type should be a unsupported media type
@@ -99,7 +98,7 @@ public async Task Wrong_Query_Should_Return_Error(HttpMethod httpMethod, HttpCon
9998
HttpMethod.Post,
10099
new StringContent(Serializer.ToJson(new GraphQLRequest { Query = "query { __schema { queryType { name } } }" }), Encoding.UTF8, "something/unknown"),
101100
HttpStatusCode.UnsupportedMediaType,
102-
"Invalid 'Content-Type' header: non-supported media type 'something/unknown; charset=utf-8'. Must be of 'application/json', 'application/graphql' or 'application/x-www-form-urlencoded'. See: http://graphql.org/learn/serving-over-http/."
101+
@"{""errors"":[{""message"":""Invalid 'Content-Type' header: non-supported media type 'something/unknown; charset=utf-8'. Must be of 'application/json', 'application/graphql' or 'application/x-www-form-urlencoded'. See: http://graphql.org/learn/serving-over-http/.""}]}"
103102
},
104103

105104
// MediaTypeHeaderValue ctor throws exception
@@ -118,7 +117,7 @@ public async Task Wrong_Query_Should_Return_Error(HttpMethod httpMethod, HttpCon
118117
HttpMethod.Post,
119118
new StringContent("Oops", Encoding.UTF8, "application/json"),
120119
HttpStatusCode.BadRequest,
121-
"JSON body text could not be parsed. 'O' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
120+
@"{""errors"":[{""message"":""JSON body text could not be parsed. 'O' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.""}]}"
122121
},
123122

124123
// POST with JSON mime type that is invalid JSON should be a bad request
@@ -127,7 +126,7 @@ public async Task Wrong_Query_Should_Return_Error(HttpMethod httpMethod, HttpCon
127126
HttpMethod.Post,
128127
new StringContent("{oops}", Encoding.UTF8, "application/json"),
129128
HttpStatusCode.BadRequest,
130-
"JSON body text could not be parsed. 'o' is an invalid start of a property name. Expected a '\"'. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
129+
@"{""errors"":[{""message"":""JSON body text could not be parsed. 'o' is an invalid start of a property name. Expected a '""'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.""}]}"
131130
},
132131

133132
// POST with JSON mime type that is null JSON should be a bad request
@@ -136,7 +135,7 @@ public async Task Wrong_Query_Should_Return_Error(HttpMethod httpMethod, HttpCon
136135
HttpMethod.Post,
137136
new StringContent("null", Encoding.UTF8, "application/json"),
138137
HttpStatusCode.BadRequest,
139-
"GraphQL query is missing."
138+
@"{""errors"":[{""message"":""GraphQL query is missing."",""extensions"":{""code"":""QUERY_MISSING"",""codes"":[""QUERY_MISSING""]}}]}"
140139
},
141140

142141
// GET with an empty QueryString should be a bad request
@@ -145,7 +144,34 @@ public async Task Wrong_Query_Should_Return_Error(HttpMethod httpMethod, HttpCon
145144
HttpMethod.Get,
146145
null,
147146
HttpStatusCode.BadRequest,
148-
"GraphQL query is missing."
147+
@"{""errors"":[{""message"":""GraphQL query is missing."",""extensions"":{""code"":""QUERY_MISSING"",""codes"":[""QUERY_MISSING""]}}]}"
148+
},
149+
150+
// POST with a GraphQL parsing error should be a bad request
151+
new object[]
152+
{
153+
HttpMethod.Post,
154+
new StringContent(@"{""query"":""parseError""}", Encoding.UTF8, "application/json"),
155+
HttpStatusCode.BadRequest,
156+
@"{""errors"":[{""message"":""Error parsing query: Expected \u0022query/mutation/subscription/fragment/schema/scalar/type/interface/union/enum/input/extend/directive\u0022, found Name \u0022parseError\u0022"",""locations"":[{""line"":1,""column"":1}],""extensions"":{""code"":""SYNTAX_ERROR"",""codes"":[""SYNTAX_ERROR""]}}]}"
157+
},
158+
159+
// POST with no operation should be a bad request
160+
new object[]
161+
{
162+
HttpMethod.Post,
163+
new StringContent(@"{""query"":""fragment frag on Query { hello }""}", Encoding.UTF8, "application/json"),
164+
HttpStatusCode.BadRequest,
165+
@"{""errors"":[{""message"":""Document does not contain any operations."",""extensions"":{""code"":""NO_OPERATION"",""codes"":[""NO_OPERATION""]}}]}"
166+
},
167+
168+
// POST with validation error should be a bad request
169+
new object[]
170+
{
171+
HttpMethod.Post,
172+
new StringContent(@"{""query"":""{ dummy }""}", Encoding.UTF8, "application/json"),
173+
HttpStatusCode.BadRequest,
174+
@"{""errors"":[{""message"":""Cannot query field \u0027dummy\u0027 on type \u0027ChatQuery\u0027."",""locations"":[{""line"":1,""column"":3}],""extensions"":{""code"":""FIELDS_ON_CORRECT_TYPE"",""codes"":[""FIELDS_ON_CORRECT_TYPE""],""number"":""5.3.1""}}]}"
149175
},
150176
};
151177

0 commit comments

Comments
 (0)