Refine handling of unresolved exceptions

Closes gh-352
This commit is contained in:
rstoyanchev
2022-04-13 17:09:10 +01:00
parent e3cf655a04
commit 304fb45fb7
3 changed files with 21 additions and 14 deletions

View File

@@ -514,21 +514,23 @@ added by <<server-interception, WebGraphQlInterceptor>> components.
[[execution-exceptions]]
=== Exception Resolution
GraphQL Java applications can register a `DataFetcherExceptionHandler` to decide how to
A GraphQL Java application can register a `DataFetcherExceptionHandler` to decide how to
represent exceptions from the data layer in the "errors" section of the GraphQL response.
Spring for GraphQL has a built-in `DataFetcherExceptionHandler` that is configured for use
by the <<execution-graphqlsource>> builder. It enables applications to register one or
more Spring `DataFetcherExceptionResolver` components that are invoked sequentially
until one resolves the `Exception` to a list of `graphql.GraphQLError` objects.
by the default <<execution-graphqlsource>> builder. It allows applications to register
one or more Spring `DataFetcherExceptionResolver` components that are invoked sequentially
until one resolves the `Exception` to a (possibly empty) list of `graphql.GraphQLError`
objects.
`DataFetcherExceptionResolver` is an asynchronous contract. For most implementations, it
would be sufficient to extend `DataFetcherExceptionResolverAdapter` and override
one of its `resolveToSingleError` or `resolveToMultipleErrors` methods that
resolve exceptions synchronously.
A `GraphQLError` can be assigned an `graphql.ErrorClassification`. Spring for GraphQL
defines an `ErrorType` enum with common, error classification categories:
A `GraphQLError` can be assigned to a category via `graphql.ErrorClassification`.
In Spring GraphQL, you can also assign via `ErrorType` which has the following common
classifications that applications can use to categorize errors:
- `BAD_REQUEST`
- `UNAUTHORIZED`
@@ -536,8 +538,14 @@ defines an `ErrorType` enum with common, error classification categories:
- `NOT_FOUND`
- `INTERNAL_ERROR`
Applications can use this to classify errors. If an error remains unresolved, by
default it is marked as `INTERNAL_ERROR`.
If an exception remains unresolved, by default it is categorized as an `INTERNAL_ERROR`
with a generic message that includes the category name and the `executionId` from
`DataFetchingEnvironment`. The message is intentionally opaque to avoid leaking
implementation details. Applications can use a `DataFetcherExceptionResolver` to customize
error details.
Unresolved exception are logged at ERROR level along with the `executionId` to correlate
to the error sent to the client. Resolved exceptions are logged at DEBUG level.

View File

@@ -35,7 +35,6 @@ import reactor.core.publisher.Mono;
import reactor.util.context.ContextView;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link DataFetcherExceptionHandler} that invokes {@link DataFetcherExceptionResolver}'s
@@ -111,14 +110,14 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler
}
private DataFetcherExceptionHandlerResult createInternalError(Throwable ex, DataFetchingEnvironment environment) {
ExecutionId executionId = environment.getExecutionId();
if (logger.isErrorEnabled()) {
ExecutionId id = environment.getExecutionId();
logger.error("Unresolved " + ex.getClass().getSimpleName() + ", executionId= " + id, ex);
logger.error("Unresolved " + ex.getClass().getSimpleName() + " for executionId " + executionId, ex);
}
return DataFetcherExceptionHandlerResult
.newResult(GraphqlErrorBuilder.newError(environment)
.errorType(ErrorType.INTERNAL_ERROR)
.message((StringUtils.hasText(ex.getMessage()) ? ex.getMessage() : ex.getClass().getSimpleName()))
.message(ErrorType.INTERNAL_ERROR + " for " + executionId)
.build())
.build();
}

View File

@@ -27,8 +27,8 @@ import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import reactor.util.context.ContextView;
import org.springframework.graphql.ResponseHelper;
import org.springframework.graphql.GraphQlSetup;
import org.springframework.graphql.ResponseHelper;
import org.springframework.graphql.TestThreadLocalAccessor;
import static org.assertj.core.api.Assertions.assertThat;
@@ -126,7 +126,7 @@ public class ExceptionResolversExceptionHandlerTests {
ResponseHelper response = ResponseHelper.forResult(result);
assertThat(response.errorCount()).isEqualTo(1);
assertThat(response.error(0).message()).isEqualTo("Invalid greeting");
assertThat(response.error(0).message()).startsWith("INTERNAL_ERROR for ");
assertThat(response.error(0).errorType()).isEqualTo("INTERNAL_ERROR");
String greeting = response.rawValue("greeting");