diff --git a/spring-graphql-docs/src/docs/asciidoc/index.adoc b/spring-graphql-docs/src/docs/asciidoc/index.adoc index 4c41e86d..8d50f979 100644 --- a/spring-graphql-docs/src/docs/asciidoc/index.adoc +++ b/spring-graphql-docs/src/docs/asciidoc/index.adoc @@ -499,25 +499,27 @@ added by <> components. [[execution.exceptions]] -=== Exception Resolution +=== Exceptions -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. +In GraphQL Java, `DataFetcherExceptionHandler` decides how to represent exceptions from +data fetching in the "errors" section of the response. An application can register a +single handler only. -Spring for GraphQL has a built-in `DataFetcherExceptionHandler` that is configured for use -by the default <> 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. +Spring for GraphQL registers a `DataFetcherExceptionHandler` that provides default +handling and enables the `DataFetcherExceptionResolver` contract. An application can +register any number of resolvers via <> builder and those are in +order until one them resolves the `Exception` to a `List`. +The Spring Boot starter detects beans of this type. -`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. +`DataFetcherExceptionResolverAdapter` is a convenient base class with protected methods +`resolveToSingleError` and `resolveToMultipleErrors`. -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: +The <> programming model enables handling data fetching exceptions with +annotated exception handler methods with a flexible method signature, see +<> for details. + +A `GraphQLError` can be assigned to a category based on the GraphQL Java +`graphql.ErrorClassification`, or the Spring GraphQL `ErrorType`, which defines the following: - `BAD_REQUEST` - `UNAUTHORIZED` @@ -534,6 +536,7 @@ 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. + [[execution.exceptions.request]] ==== Request Exceptions @@ -1662,6 +1665,88 @@ Batch mapping methods can return: +[[controllers.exception-handler]] +=== `@GraphQlExceptionHandler` + +Use `@GraphQlExceptionHandler` methods to handle exceptions from data fetching with a +flexible <>. When declared in a +controller, exception handler methods apply to exceptions from the same controller: + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @Controller + public class BookController { + + @QueryMapping + public Book bookById(@Argument Long id) { + // ... + } + + @GraphQlExceptionHandler + public GraphQLError handle(BindException ex) { + return GraphQLError.newError().errorType(ErrorType.BAD_REQUEST).message("...").build(); + } + + } +---- + +When declared in an `@ControllerAdvice`, exception handler methods apply across controllers: + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @ControllerAdvice + public class GlobalExceptionHandler { + + @GraphQlExceptionHandler + public GraphQLError handle(BindException ex) { + return GraphQLError.newError().errorType(ErrorType.BAD_REQUEST).message("...").build(); + } + + } +---- + +Exception handling via `@GraphQlExceptionHandler` methods is applied automatically to +controller invocations. To handle exceptions from other `graphql.schema.DataFetcher` +implementations, not based on controller methods, obtain a +`DataFetcherExceptionResolver` from `AnnotatedControllerConfigurer`, and register it in +`GraphQlSource.Builder` as a <>. + + + + +[[controllers.exception-handler.signature]] +==== Method Signature + +Exception handler methods support a flexible method signature with method arguments +resolved from a `DataFetchingEnvironment,` and matching to those of +<>. + +Supported return types are listed below: + +[cols="1,2"] +|=== +| Return Type | Description + +| `graphql.GraphQLError` +| Resolve the exception to a single field error. + +| `Collection` +| Resolve the exception to multiple field errors. + +| `void` +| Resolve the exception without response errors. + +| `Object` +| Resolve the exception to a single error, to multiple errors, or none. + The return value must be `GraphQLError`, `Collection`, or `null`. + +| `Mono` +| For asynchronous resolution where `` is one of the supported, synchronous, return types. + +|=== + + + [[security]] == Security diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/GraphQlExceptionHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/GraphQlExceptionHandler.java index 383f5773..d2198fac 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/GraphQlExceptionHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/GraphQlExceptionHandler.java @@ -38,6 +38,9 @@ import java.util.List; * {@link org.springframework.graphql.execution.GraphQlSource.Builder#exceptionResolvers(List) * GraphQlSource.Builder}. * + *

Supported return types are listed in the Spring for GraphQL reference documentation + * in the section {@literal "Annotated Controllers"}. + * * @author Rossen Stoyanchev * @since 1.2 */ diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java index 632637ba..60e2fbe3 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java @@ -367,7 +367,7 @@ final class AnnotatedControllerExceptionResolver { */ static ReturnValueAdapter createFor(MethodParameter returnType) { Class parameterType = returnType.getParameterType(); - if (parameterType == void.class || parameterType == Void.class) { + if (parameterType == void.class) { return forVoid; } else if (parameterType.equals(GraphQLError.class)) { @@ -381,7 +381,7 @@ final class AnnotatedControllerExceptionResolver { else if (Mono.class.isAssignableFrom(parameterType)) { returnType = returnType.nested(); Class nestedType = returnType.getNestedParameterType(); - if (nestedType == void.class || nestedType == Void.class) { + if (nestedType == Void.class) { return forMonoVoid; } if (Collection.class.isAssignableFrom(nestedType)) {