繁体   English   中英

如何处理 Spring Boot 中的 GraphQL 查询验证错误

[英]How to handle GraphQL query validation error in Spring Boot

我有一个使用spring-boot-starter-graphql的简单 Spring Boot 项目。 这个项目有一个控制器,它接受一个参数。

@Controller
public class HelloNameController {

    @QueryMapping
    public String hello(@Argument String name) {
        return "Hello " + name;
    }
}

该参数是必需的。

Graphql 架构

 type Query {
     hello (name : String!) : String
  }

当我在 Postman 中调用此 API 并且不传递此参数时,应用程序会返回错误。 我想覆盖此错误消息的消息,但找不到方法。 在官方文档中,它说要实现DataFetcherExceptionResolverAdapter ,我已经将它实现为 bean

@Configuration
public class GraphQLConfig {

    @Bean
    public DataFetcherExceptionResolver exceptionResolver() {
        return DataFetcherExceptionResolverAdapter.from((ex, env) -> {
            if (ex instanceof CoercingParseValueException) {

                return GraphqlErrorBuilder.newError(env).message("CoercingParseValueException")
                        .errorType(ErrorType.ExecutionAborted).build();
            }
            if (ex instanceof CoercingSerializeException) {
                return GraphqlErrorBuilder.newError(env).message("CoercingSerializeException")
                        .errorType(ErrorType.ExecutionAborted).build();
            } else {
                return null;
            }
        });
    }
}

问题是错误永远不会到达这一点。 如何捕获此类错误并覆盖该消息?

我在 GitHub 上问过类似的问题。 graphql-java 项目 ( #2866 ) 和 spring-graphql 项目 ( #415 ) 的响应相似。 在撰写本文时进行总结是不可能的。 然后我创建了一个“解决方法”:

首先,创建一个实现 GraphQLError 的自定义异常类。

import graphql.GraphQLError;
import graphql.language.SourceLocation;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.http.HttpStatus;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Getter
@NoArgsConstructor
public class BadRequestException extends RuntimeException implements GraphQLError {

    private HttpStatus status = HttpStatus.BAD_REQUEST;

    private String message = "Resource not found";

    // Below code used for GraphQL only
    private List<SourceLocation> locations;

    public BadRequestException(String message, List<SourceLocation> locations) {
        this.message = message;
        this.locations = locations;
    }

    @Override
    public Map<String, Object> getExtensions() {
        Map<String, Object> customAttributes = new LinkedHashMap<>();
        customAttributes.put("errorCode", this.status.value());
        return customAttributes;
    }

    @Override
    public List<SourceLocation> getLocations() {
        return locations;
    }

    @Override
    public ErrorType getErrorType() {
        return ErrorType.BAD_REQUEST;
    }

    @Override
    public Map<String, Object> toSpecification() {
        return GraphQLError.super.toSpecification();
    }

}

其次,创建一个实现WebGraphQlInterceptor的拦截器类,并将其注解为@Component,这样Spring就可以将其创建为一个bean。 在此类内部实现逻辑以捕获所需的错误并将其转换为之前创建的异常类

import graphql.ErrorClassification;
import graphql.ErrorType;
import graphql.GraphQLError;
import graphql.validation.ValidationErrorType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.graphql.ResponseError;
import org.springframework.graphql.server.WebGraphQlInterceptor;
import org.springframework.graphql.server.WebGraphQlRequest;
import org.springframework.graphql.server.WebGraphQlResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Component
public class ErrorInterceptor implements WebGraphQlInterceptor {

    @Override
    public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
        return chain.next(request)
                .map(response -> {
                    log.info("[ErrorInterceptor] Intercepting response... ");

                    List<GraphQLError> graphQLErrors = response.getErrors().stream()
                            .filter(error -> ErrorType.ValidationError.equals(error.getErrorType()))
                            .map(this::resolveException)
                            .collect(Collectors.toList());

                    if (!graphQLErrors.isEmpty()) {
                        log.info("[ErrorInterceptor] Found invalid syntax error! Overriding the message.");
                        return response.transform(builder -> builder.errors(graphQLErrors));
                    }

                    return response;
                });
    }

    private GraphQLError resolveException(ResponseError responseError) {

        ErrorClassification errorType = responseError.getErrorType();

        if (ErrorType.ValidationError.equals(errorType)) {
            String message = responseError.getMessage();
            log.info("[ErrorInterceptor] Returning invalid field error ");

            if (ValidationErrorType.NullValueForNonNullArgument.equals(
                    extractValidationErrorFromErrorMessage(responseError.getMessage()))) {
                String errorMessage =
                        "Field " + StringUtils.substringBetween(message, "argument ", " @") + " cannot be null";
                return new BadRequestException(errorMessage, responseError.getLocations());
            }
        }

        log.info("[ErrorInterceptor] Returning unknown query validation error ");
        return new BadRequestException("Unknown error", responseError.getLocations());
    }

    private ValidationErrorType extractValidationErrorFromErrorMessage(String message) {
        return ValidationErrorType.valueOf(StringUtils.substringBetween(message, "type ", ":"));
    }

}

这种方法的唯一问题是,所有需要的信息(如错误类型、导致错误的字段等)都嵌入在本机错误消息中。 因此,要提取所需的参数,我们必须解析字符串消息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM