如何处理自定义 GraphQL 错误,使其不在 Java 中显示异常堆栈跟踪

How to handle custom GraphQL error such that it does not display the exception stack trace in Java

我使用 Spring Boot 创建了一个简单的 GraphQL 端点,我正在使用 DefaultGraphQLErrorHandler() 来处理 GraphQL 错误。

但是,当我从我的应用程序中抛出自定义异常时,GraphQL 生成的错误响应包含异常堆栈跟踪,这会泄露太多信息。我想阻止这种情况。

{
"data": {
    "CommercialAsset": null
},
"errors": [
    {
        "message": "Exception while fetching data (/CommercialAsset) : Asset not fround in Data source",
        "path": [
            "CommercialAsset"
        ],
        "exception": {
            "cause": null,
            "stackTrace": [
                {
                    "classLoaderName": null,
                    "moduleName": null,
                    "moduleVersion": null,
                    "methodName": "getAssetById",
                    "fileName": "CommercialAssetDremioRepositoryImpl.java",
                    "lineNumber": 49,
                    "className": "com.dell.dremioclient.repository.impl.CommercialAssetDremioRepositoryImpl",
                    "nativeMethod": false
                },
                ...
                {
                    "classLoaderName": null,
                    "moduleName": "java.base",
                    "moduleVersion": "11.0.14",
                    "methodName": "run",
                    "fileName": "Thread.java",
                    "lineNumber": 834,
                    "className": "java.lang.Thread",
                    "nativeMethod": false
                }
            ],
            "message": "Asset not fround in Data source",
            "locations": null,
            "errorType": null,
            "path": null,
            "extensions": null,
            "suppressed": [],
            "localizedMessage": "Asset not fround in Data source"
        },
        "locations": [
            {
                "line": 2,
                "column": 5,
                "sourceName": null
            }
        ],
        "extensions": null,
        "errorType": "DataFetchingException"
    }
]

}

我正在使用的 GraphQL 依赖版本:

    <dependency>
       <groupId>com.graphql-java</groupId>
        <artifactId>graphql-spring-boot-starter</artifactId>
        <version>5.0.2</version>
    </dependency>
    <dependency>
        <groupId>com.graphql-java</groupId>
        <artifactId>graphql-java-tools</artifactId>
        <version>5.2.4</version>
    </dependency>
    <dependency>
        <groupId>com.graphql-java</groupId>
        <artifactId>graphiql-spring-boot-starter</artifactId>
        <version>5.0.2</version>
    </dependency>

有什么方法可以使用自定义错误处理程序吗?像这样 -

public class CustomGraphqlErrorHandler implements GraphQLErrorHandler {

@Override
public List<GraphQLError> processErrors(List<GraphQLError> errors) {
    
    List<GraphQLError> errorList = new ArrayList<>();
    
    errors.stream()
            .forEach( e -> {
                if(this.isServerError(e)) {
                    GraphqlDremioClientException gexp = new GraphqlDremioClientException(e.getMessage());
                    gexp.setStackTrace(null); /* This causes failure Bad POST request: parsing failed
                                                 java.lang.NullPointerException: null
                                                 at java.base/java.lang.Throwable.setStackTrace(Throwable.java:865) */
                    errorList.add(gexp);
                } else {
                    errorList.add(e);
                }
            });
    
    return errorList;
}

private boolean isServerError(GraphQLError error) {
    return (error instanceof ExceptionWhileDataFetching || error instanceof Throwable);
}

@Override
public boolean errorsPresent(List<GraphQLError> errors) {
    return !CollectionUtils.isEmpty(errors);
}

}

我想阻止 GraphQL 在错误响应中显示堆栈跟踪。一个简单的解决方案是添加自定义 GraphQL 错误处理程序来处理从我的服务中抛出的异常。然后,我创建了一个自定义异常 class,它可以在构造期间启用或禁用堆栈跟踪。

自定义异常 class:

public class GraphqlDremioClientException extends RuntimeException implements GraphQLError {

    private static final long serialVersionUID = 1L;
    
    private final String message;
    
    private boolean writeStacktrace = false;

    @Override
    public String getMessage() {
        return message;
    }
    /* other constructors */
        
    public GraphqlDremioClientException(String message, boolean writeStacktrace) {
        super(message, null, false, writeStacktrace);
        this.writeStacktrace = writeStacktrace;
        this.message = message;
    }
    
    public GraphqlDremioClientException(String message, Exception ex) {
        super();
        this.message = message;
    }

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

    @Override
    public ErrorType getErrorType() {
        return null;
    }       
}

自定义 GraphQL 错误处理程序:

@Slf4j
@Component
public class CustomGraphqlErrorHandler implements GraphQLErrorHandler {

    @Override
    public List<GraphQLError> processErrors(List<GraphQLError> list) {
        return list.stream().map(this::getNested).collect(Collectors.toList());
    }
    
    private GraphQLError getNested(GraphQLError error) {
        log.error(error.getMessage(), error);
        
        if (error instanceof ExceptionWhileDataFetching) {
            ExceptionWhileDataFetching exceptionError = (ExceptionWhileDataFetching) error;
            if (exceptionError.getException() instanceof GraphQLError) {
                return new GraphqlDremioClientException(exceptionError.getMessage(), false);
            }
        }
        return error;
    }
}

现在的错误响应:

{
    "data": {
        "CommercialAsset": null
    },
    "errors": [
        {
            "cause": null,
            "stackTrace": [],
            "message": "Exception while fetching data (/CommercialAsset) : Asset not fround in Data source",
            "writeStacktrace": false,
            "locations": null,
            "errorType": null,
            "path": null,
            "extensions": null,
            "suppressed": [],
            "localizedMessage": "Exception while fetching data (/CommercialAsset) : Asset not fround in Data source"
        }
    ]
}