Skip to content

Commit

Permalink
Replicate changes of #3783 for v11 (#3787)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vec7or authored Jun 3, 2021
1 parent d88c81d commit 4d8ddbb
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ private async Task HandleRequestAsync(HttpContext context)
statusCode = HttpStatusCode.BadRequest;
result = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors));
}
catch (GraphQLException ex)
{
// This allows extensions to throw GraphQL exceptions in the GraphQL interceptor.
statusCode = null; // we let the serializer determine the status code.
result = QueryResultBuilder.CreateError(ex.Errors);
}
catch (Exception ex)
{
statusCode = HttpStatusCode.InternalServerError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private static HttpMultipartRequest ParseMultipartRequest(IFormCollection form)

try
{
map = JsonSerializer.Deserialize<Dictionary<string, string[]>>(mapString);
map = JsonSerializer.Deserialize<Dictionary<string, string[]>>(mapString!);
}
catch
{
Expand Down Expand Up @@ -176,7 +176,7 @@ private static void InsertFilesIntoRequest(
{
var path = VariablePath.Parse(objectPath);

if (!mutableVariables.TryGetValue(path.Key.Value, out object? value))
if (!mutableVariables.TryGetValue(path.Key.Value, out var value))
{
throw ThrowHelper.HttpMultipartMiddleware_VariableNotFound(objectPath);
}
Expand Down Expand Up @@ -206,8 +206,7 @@ private static IValueNode RewriteVariable(
object value,
FileValueNode file)
{
if (segment is KeyPathSegment key &&
value is ObjectValueNode ov)
if (segment is KeyPathSegment key && value is ObjectValueNode ov)
{
var pos = -1;

Expand All @@ -234,8 +233,7 @@ key.Next is not null
return ov.WithFields(fields);
}

if (segment is IndexPathSegment index &&
value is ListValueNode lv)
if (segment is IndexPathSegment index && value is ListValueNode lv)
{
IValueNode[] items = lv.Items.ToArray();
IValueNode item = items[index.Value];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ protected async Task HandleRequestAsync(
statusCode = HttpStatusCode.BadRequest;
result = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors));
}
catch (GraphQLException ex)
{
// This allows extensions to throw GraphQL exceptions in the GraphQL interceptor.
statusCode = null; // we let the serializer determine the status code.
result = QueryResultBuilder.CreateError(ex.Errors);
}
catch (Exception ex)
{
statusCode = HttpStatusCode.InternalServerError;
Expand All @@ -145,7 +151,7 @@ protected async Task HandleRequestAsync(

// in any case we will have a valid GraphQL result at this point that can be written
// to the HTTP response stream.
Debug.Assert(result is not null, "No GraphQL result was created.");
Debug.Assert(result is not null!, "No GraphQL result was created.");
await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using HotChocolate.AspNetCore.Utilities;
using HotChocolate.Execution;
using HotChocolate.Language;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Snapshooter;
using Snapshooter.Xunit;
using Xunit;
Expand Down Expand Up @@ -697,5 +699,41 @@ await server.GetStoreActivePersistedQueryAsync(
resultB
}.MatchSnapshot();
}

[Fact]
public async Task Throw_Custom_GraphQL_Error()
{
// arrange
TestServer server = CreateStarWarsServer(
configureServices: s => s.AddGraphQLServer()
.AddHttpRequestInterceptor<ErrorRequestInterceptor>());

// act
ClientQueryResult result =
await server.GetAsync(new ClientQueryRequest
{
Query = @"
{
hero {
name
}
}"
});

// assert
result.MatchSnapshot();
}

public class ErrorRequestInterceptor : DefaultHttpRequestInterceptor
{
public override ValueTask OnCreateAsync(
HttpContext context,
IRequestExecutor requestExecutor,
IQueryRequestBuilder requestBuilder,
CancellationToken cancellationToken)
{
throw new GraphQLException("MyCustomError");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.TestHost;
using HotChocolate.AspNetCore.Utilities;
using HotChocolate.Execution;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Snapshooter;
using Snapshooter.Xunit;
Expand Down Expand Up @@ -68,8 +71,8 @@ public async Task Simple_IsAlive_Test_On_Non_GraphQL_Path()

// act
ClientQueryResult result = await server.PostAsync(
new ClientQueryRequest { Query = "{ __typename }" },
"/foo");
new ClientQueryRequest { Query = "{ __typename }" },
"/foo");

// assert
result.MatchSnapshot();
Expand Down Expand Up @@ -135,10 +138,7 @@ await server.PostAsync(new ClientQueryRequest
name
}
}",
Variables = new Dictionary<string, object>
{
{ "episode", "NEW_HOPE" }
}
Variables = new Dictionary<string, object> { { "episode", "NEW_HOPE" } }
});

// assert
Expand All @@ -161,10 +161,7 @@ query h($id: String!) {
name
}
}",
Variables = new Dictionary<string, object>
{
{ "id", "1000" }
}
Variables = new Dictionary<string, object> { { "id", "1000" } }
});

// assert
Expand Down Expand Up @@ -224,8 +221,7 @@ mutation CreateReviewForEpisode(
"review",
new Dictionary<string, object>
{
{ "stars", 5 },
{ "commentary", "This is a great movie!" },
{ "stars", 5 }, { "commentary", "This is a great movie!" },
}
}
}
Expand Down Expand Up @@ -260,8 +256,7 @@ mutation CreateReviewForEpisode(
"review",
new Dictionary<string, object>
{
{ "stars", 5 },
{ "commentary", "This is a great movie!" },
{ "stars", 5 }, { "commentary", "This is a great movie!" },
}
}
}
Expand Down Expand Up @@ -390,10 +385,7 @@ await server.PostAsync(new ClientQueryRequest
name
}
}",
Variables = new Dictionary<string, object>
{
{ "episode", "NEW_HOPE" }
}
Variables = new Dictionary<string, object> { { "episode", "NEW_HOPE" } }
});

// assert
Expand Down Expand Up @@ -463,11 +455,7 @@ await server.PostAsync(new ClientQueryRequest
"/arguments");

// assert
new
{
double.MaxValue,
result
}.MatchSnapshot();
new { double.MaxValue, result }.MatchSnapshot();
}

[Fact]
Expand All @@ -489,11 +477,7 @@ await server.PostAsync(new ClientQueryRequest
"/arguments");

// assert
new
{
double.MinValue,
result
}.MatchSnapshot();
new { double.MinValue, result }.MatchSnapshot();
}

[Fact]
Expand All @@ -515,11 +499,7 @@ await server.PostAsync(new ClientQueryRequest
"/arguments");

// assert
new
{
decimal.MaxValue,
result
}.MatchSnapshot();
new { decimal.MaxValue, result }.MatchSnapshot();
}

[Fact]
Expand All @@ -541,11 +521,7 @@ await server.PostAsync(new ClientQueryRequest
"/arguments");

// assert
new
{
decimal.MinValue,
result
}.MatchSnapshot();
new { decimal.MinValue, result }.MatchSnapshot();
}

[Fact]
Expand Down Expand Up @@ -778,5 +754,41 @@ query getHuman {
// assert
result.MatchSnapshot();
}

[Fact]
public async Task Throw_Custom_GraphQL_Error()
{
// arrange
TestServer server = CreateStarWarsServer(
configureServices: s => s.AddGraphQLServer()
.AddHttpRequestInterceptor<ErrorRequestInterceptor>());

// act
ClientQueryResult result =
await server.PostAsync(new ClientQueryRequest
{
Query = @"
{
hero {
name
}
}"
});

// assert
result.MatchSnapshot();
}

public class ErrorRequestInterceptor : DefaultHttpRequestInterceptor
{
public override ValueTask OnCreateAsync(
HttpContext context,
IRequestExecutor requestExecutor,
IQueryRequestBuilder requestBuilder,
CancellationToken cancellationToken)
{
throw new GraphQLException("MyCustomError");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"ContentType": "application/json; charset=utf-8",
"StatusCode": "InternalServerError",
"Data": null,
"Errors": [
{
"message": "MyCustomError"
}
],
"Extensions": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"ContentType": "application/json; charset=utf-8",
"StatusCode": "InternalServerError",
"Data": null,
"Errors": [
{
"message": "MyCustomError"
}
],
"Extensions": null
}
10 changes: 10 additions & 0 deletions website/src/docs/hotchocolate/api-reference/aspnetcore.md
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,16 @@ public string MyResolver([HttpContext] HttpContext context)
}
```

A custom http request interceptor can be registered like the following:

```csharp
services.AddSocketSessionInterceptor<MyCustomHttpRequestInterceptor>();
```

## Request Errors

The interceptor can be used to do general request validation. This essentially allows to fail the request before the GraphQL context is created. In order to create a GraphQL error response simply throw a `GraphQLException` in the `OnCreateAsync` method. The middleware will translate these to a proper GraphQL error response for the client. You also can customize the status code behavior by using the HTTP result serializer mentioned above.

# Subscription session handling

The Hot Chocolate GraphQL server allows you to interact with the server's socket session handling by implementing `ISocketSessionInterceptor`. For convenience reasons, we provide a default implementation (`DefaultSocketSessionInterceptor`) that can be extended.
Expand Down

0 comments on commit 4d8ddbb

Please sign in to comment.