Skip to content

Commit b17aca0

Browse files
authored
Reworked Status Code Behavior (#7117)
1 parent 4340927 commit b17aca0

9 files changed

+58
-33
lines changed

src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,13 @@ public async ValueTask FormatAsync(
184184
case OperationResultBatch resultBatch:
185185
{
186186
var statusCode = (int)OnDetermineStatusCode(resultBatch, format, proposedStatusCode);
187-
187+
188188
response.ContentType = format.ContentType;
189189
response.StatusCode = statusCode;
190190
response.Headers.CacheControl = HttpHeaderValues.NoCache;
191191
OnWriteResponseHeaders(resultBatch, format, response.Headers);
192192
await response.Body.FlushAsync(cancellationToken);
193-
193+
194194
await format.Formatter.FormatAsync(result, response.Body, cancellationToken);
195195
break;
196196
}
@@ -284,7 +284,7 @@ protected virtual HttpStatusCode OnDetermineStatusCode(
284284
}
285285

286286
// next we check if the validation of the request failed.
287-
// if that is the case we will we will return a BadRequest status code (400).
287+
// if that is the case we will return a BadRequest status code (400).
288288
if (contextData.ContainsKey(ValidationErrors))
289289
{
290290
return HttpStatusCode.BadRequest;
@@ -296,22 +296,21 @@ protected virtual HttpStatusCode OnDetermineStatusCode(
296296
}
297297
}
298298

299-
// if data is not null then we have a valid result. The result of executing
300-
// a GraphQL operation may contain partial data as well as encountered errors.
301-
// Errors that happen during execution of the GraphQL operation typically
302-
// become part of the result, as long as the server is still able to produce
303-
// a well-formed response.
304-
if (result.Data is not null)
299+
// if data is set then the execution as begun and has produced a result.
300+
// The result of executing GraphQL operation may contain partial data as
301+
// well as encountered errors. Errors that happen during execution of the
302+
// GraphQL operation typically become part of the result, as long as the
303+
// server is still able to produce a well-formed response.
304+
// Even null represents a valid response, in this case of a non-null propagation
305+
// that erased the result.
306+
if (result.IsDataSet)
305307
{
306308
return HttpStatusCode.OK;
307309
}
308310

309-
// if data is null we consider the result not valid and return a 500 if the user did
310-
// not override the status code with a different status code.
311-
// this is however at the moment a point of discussion as there are opposing views
312-
// towards what constitutes a valid response.
313-
// we will update this status code as the spec moves towards release.
314-
return HttpStatusCode.InternalServerError;
311+
// if data was never set the result not valid and execution has never started, and we return a 400
312+
// if the user did not override the status code with a different status code.
313+
return HttpStatusCode.BadRequest;
315314
}
316315

317316
// we allow for users to implement alternative protocols or response content-type.
@@ -389,7 +388,7 @@ protected virtual void OnWriteResponseHeaders(
389388
IResponseStream responseStream,
390389
FormatInfo format,
391390
IHeaderDictionary headers) { }
392-
391+
393392
/// <summary>
394393
/// Determines which status code shall be returned for a result batch.
395394
/// </summary>
@@ -626,9 +625,6 @@ private enum ResultKind
626625
Subscription,
627626
}
628627

629-
private sealed class SealedDefaultHttpResponseFormatter : DefaultHttpResponseFormatter
630-
{
631-
public SealedDefaultHttpResponseFormatter(HttpResponseFormatterOptions options)
632-
: base(options) { }
633-
}
628+
private sealed class SealedDefaultHttpResponseFormatter(HttpResponseFormatterOptions options)
629+
: DefaultHttpResponseFormatter(options);
634630
}

src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ protected virtual TestServer CreateStarWarsServer(
7676
.AddDiagnosticEventListener(_ => new SubscriptionTestDiagnostics(output));
7777
}
7878

79+
services
80+
.AddGraphQLServer("notnull")
81+
.AddQueryType(c =>
82+
{
83+
c.Name("Query");
84+
c.Field("error")
85+
.Type<NonNullType<StringType>>()
86+
.Resolve(_ => Task.FromResult<object?>(null!));
87+
});
88+
7989
configureServices?.Invoke(services);
8090
},
8191
app => app
@@ -87,7 +97,7 @@ protected virtual TestServer CreateStarWarsServer(
8797
#if NET8_0_OR_GREATER
8898
endpoints.MapGraphQLPersistedOperations();
8999
#endif
90-
100+
91101
var builder = endpoints.MapGraphQL(pattern)
92102
.WithOptions(new GraphQLServerOptions
93103
{
@@ -96,6 +106,7 @@ protected virtual TestServer CreateStarWarsServer(
96106
});
97107

98108
configureConventions?.Invoke(builder);
109+
endpoints.MapGraphQL("/notnull", "notnull");
99110
endpoints.MapGraphQL("/evict", "evict");
100111
endpoints.MapGraphQL("/arguments", "arguments");
101112
endpoints.MapGraphQL("/upload", "upload");

src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ public async Task DeferredQuery_EventStream(string acceptHeader)
425425
}
426426

427427
[Fact]
428-
public async Task DefferedQuery_NoStreamableAcceptHeader()
428+
public async Task DeferredQuery_NoStreamableAcceptHeader()
429429
{
430430
// arrange
431431
var server = CreateStarWarsServer();
@@ -591,6 +591,24 @@ public async Task VariableBatch()
591591
await snapshot.MatchMarkdownAsync();
592592
}
593593

594+
[Fact]
595+
public async Task After_Execution_StatusCode_Is_200()
596+
{
597+
// arrange
598+
var server = CreateStarWarsServer();
599+
var client = new DefaultGraphQLHttpClient(server.CreateClient());
600+
601+
// act
602+
var request = new GraphQLHttpRequest(
603+
new OperationRequest("{ error }"),
604+
new Uri("http://localhost:5000/notnull"));
605+
606+
using var response = await client.SendAsync(request);
607+
608+
// assert
609+
Assert.Equal(response.StatusCode, OK);
610+
}
611+
594612
private HttpClient GetClient(HttpTransportVersion serverTransportVersion)
595613
{
596614
var server = CreateStarWarsServer(

src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetMiddlewareTests.SingleRequest_CreateReviewForEpisode_Omit_NonNull_Variable.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ContentType": "application/graphql-response+json; charset=utf-8",
3-
"StatusCode": "InternalServerError",
3+
"StatusCode": "BadRequest",
44
"Data": null,
55
"Errors": [
66
{

src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetMiddlewareTests.Throw_Custom_GraphQL_Error.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ContentType": "application/graphql-response+json; charset=utf-8",
3-
"StatusCode": "InternalServerError",
3+
"StatusCode": "BadRequest",
44
"Data": null,
55
"Errors": [
66
{

src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpPostMiddlewareTests.SingleRequest_CreateReviewForEpisode_Omit_NonNull_Variable.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ContentType": "application/graphql-response+json; charset=utf-8",
3-
"StatusCode": "InternalServerError",
3+
"StatusCode": "BadRequest",
44
"Data": null,
55
"Errors": [
66
{

src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpPostMiddlewareTests.Throw_Custom_GraphQL_Error.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ContentType": "application/graphql-response+json; charset=utf-8",
3-
"StatusCode": "InternalServerError",
3+
"StatusCode": "BadRequest",
44
"Data": null,
55
"Errors": [
66
{

src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteOperationScope.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ protected override void EnrichActivity()
1919

2020
protected override void SetStatus()
2121
{
22-
if (Context.Result is not null and not IOperationResult { Data: null, })
22+
if (Context.Result is null or IOperationResult { Errors: [_, ..] })
2323
{
24-
Activity.SetStatus(Status.Ok);
25-
Activity.SetStatus(ActivityStatusCode.Ok);
24+
Activity.SetStatus(Status.Error);
25+
Activity.SetStatus(ActivityStatusCode.Error);
2626
}
2727
else
2828
{
29-
Activity.SetStatus(Status.Error);
30-
Activity.SetStatus(ActivityStatusCode.Error);
29+
Activity.SetStatus(Status.Ok);
30+
Activity.SetStatus(ActivityStatusCode.Ok);
3131
}
3232
}
3333
}

src/HotChocolate/Diagnostics/src/Diagnostics/Scopes/ExecuteRequestScope.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ protected override void EnrichActivity()
1919

2020
protected override void SetStatus()
2121
{
22-
if (Context.Result is IOperationResult { Data: null, })
22+
if (Context.Result is null or IOperationResult { Errors: [_, ..] })
2323
{
2424
Activity.SetStatus(Status.Error);
2525
Activity.SetStatus(ActivityStatusCode.Error);

0 commit comments

Comments
 (0)