GraphQL Java отправить пользовательскую ошибку в формате JSON - PullRequest
0 голосов
/ 29 августа 2018

Я работаю в приложении graphql, где мне приходится отправлять пользовательский объект / сообщение об ошибке в json независимо от того, происходит ли это в сервлете или службе.

Ожидаемое сообщение об ошибке

{ errorCode: 400 //error goes here, errorMessage: "my error mesage"}

Будет полезно, если кто-нибудь поможет мне выполнить вышеуказанное требование.

Ответы [ 2 ]

0 голосов
/ 22 марта 2019

Другой опубликованный ответ не работал для меня. Я нашел решение, создав следующие классы:

1) Выбрасываемый CustomException типа GraphQLError (как упомянуто в другом ответе).

2) Создание адаптера GraphQLError, который не является Throwable.

3) Пользовательский GraphQLErrorHandler для фильтрации пользовательского исключения.

Шаг 1:
Нижеприведенный CustomGraphQLException реализует GraphQLError, поскольку интерфейс GraphQLErrorHandler принимает ошибки только типа GraphQLError.

public class CustomGraphQLException extends RuntimeException implements GraphQLError {

    private final int errorCode;
    private final String errorMessage;

    public CustomGraphQLException(int errorCode, String errorMessage) {
        super(errorMessage);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

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

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

    @Override
    public String getMessage() {
        return this.errorMessage;
    }

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

Шаг 2:
Адаптер без выброса GraphQLError создан, чтобы избежать трассировки стека вышеупомянутого пользовательского исключения, передаваемого в окончательном ответе об ошибке GraphQL.

public class GraphQLErrorAdaptor implements GraphQLError {

    private final GraphQLError graphQLError;

    public GraphQLErrorAdaptor(GraphQLError graphQLError) {
        this.graphQLError = graphQLError;
    }

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

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

    @Override
    public String getMessage() {
        return graphQLError.getMessage();
    }

    @Override
    public Map<String, Object> getExtensions() {
        return graphQLError.getExtensions();
    }
}

Шаг 3:
Пользовательский GraphQLErrorHandler реализован для фильтрации пользовательского CustomGraphQLException и предотвращения его замены ответом об ошибке graphQL по умолчанию.

public class CustomGraphQLErrorHandler implements GraphQLErrorHandler {

    public CustomGraphQLErrorHandler() { }

    public List<GraphQLError> processErrors(List<GraphQLError> errors) {
        List<GraphQLError> clientErrors = this.filterGraphQLErrors(errors);
        List<GraphQLError> internalErrors = errors.stream()
                .filter(e -> isInternalError(e))
                .map(GraphQLErrorAdaptor::new)
                .collect(Collectors.toList());

        if (clientErrors.size() + internalErrors.size() < errors.size()) {
            clientErrors.add(new GenericGraphQLError("Internal Server Error(s) while executing query"));
            errors.stream().filter((error) -> !this.isClientError(error)
            ).forEach((error) -> {
                if (error instanceof Throwable) {
                    LOG.error("Error executing query!", (Throwable) error);
                } else {
                    LOG.error("Error executing query ({}): {}", error.getClass().getSimpleName(), error.getMessage());
                }

            });
        }
        List<GraphQLError> finalErrors = new ArrayList<>();
        finalErrors.addAll(clientErrors);
        finalErrors.addAll(internalErrors);

        return finalErrors;
    }

    protected List<GraphQLError> filterGraphQLErrors(List<GraphQLError> errors) {
        return errors.stream().filter(this::isClientError).collect(Collectors.toList());
    }

    protected boolean isClientError(GraphQLError error) {
        return !(error instanceof ExceptionWhileDataFetching) && !(error instanceof Throwable);
    }

    protected boolean isInternalError(GraphQLError error) {
        return (error instanceof ExceptionWhileDataFetching) &&
                (((ExceptionWhileDataFetching) error).getException() instanceof CustomGraphQLException);
    }

}

Шаг 4: Настройте CustomGraphQLErrorHandler в GraphQLServlet. Я предполагаю, что вы используете spring-boot для этого шага.

@Configuration
public class GraphQLConfig {

    @Bean
    public ServletRegistrationBean graphQLServletRegistrationBean(
            QueryResolver queryResolver,
            CustomGraphQLErrorHandler customGraphQLErrorHandler) throws Exception {

        GraphQLSchema schema = SchemaParser.newParser()
                .schemaString(IOUtils.resourceToString("/library.graphqls", Charset.forName("UTF-8")))
                .resolvers(queryResolver)
                .build()
                .makeExecutableSchema();

        return new ServletRegistrationBean(new SimpleGraphQLServlet(schema,
                new DefaultExecutionStrategyProvider(), null, null, null,
                customGraphQLErrorHandler, new DefaultGraphQLContextBuilder(), null,
                null), "/graphql");

    }

}

Ссылка

0 голосов
/ 05 октября 2018

Спецификация GraphQL определяет четкий формат для записи error в ответе.

В соответствии со спецификацией, оно должно выглядеть так (при условии использования формата JSON):

  "errors": [
    {
      "message": "Name for character with ID 1002 could not be fetched.",
      "locations": [ { "line": 6, "column": 7 } ],
      "path": [ "hero", "heroFriends", 1, "name" ]
      "extension": {/* You can place data in any format here */}
    }
  ]

Таким образом, вы не найдете реализацию GraphQL, которая позволяет расширять ее и возвращать что-то подобное в результате выполнения GraphQL, например:

  "errors": [
    {
      "errorMessage": "Name for character with ID 1002 could not be fetched.",
      "errorCode": 404
    }
  ]

Однако спецификация позволяет добавлять данные в любом формате в запись extension. Таким образом, вы можете создать пользовательское исключение на стороне сервера и получить ответ, который выглядит следующим образом в JSON:

  "errors": [
    {
      "message": "Name for character with ID 1002 could not be fetched.",
      "locations": [ { "line": 6, "column": 7 } ],
      "path": [ "hero", "heroFriends", 1, "name" ]
      "extension": {
          "errorMessage": "Name for character with ID 1002 could not be fetched.",
          "errorCode": 404
      }
    }
  ]

Это довольно легко реализовать на GraphQL Java, как описано в документах . Вы можете создать пользовательское исключение, которое переопределяет метод getExtensions, и создать внутри реализации карту, которая затем будет использоваться для создания содержимого extensions:

public class CustomException extends RuntimeException implements GraphQLError {
    private final int errorCode;

    public CustomException(int errorCode, String errorMessage) {
        super(errorMessage);

        this.errorCode = errorCode;
    }

    @Override
    public Map<String, Object> getExtensions() {
        Map<String, Object> customAttributes = new LinkedHashMap<>();

        customAttributes.put("errorCode", this.errorCode);
        customAttributes.put("errorMessage", this.getMessage());

        return customAttributes;
    }

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

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

тогда вы можете выдать исключение, передавая код и сообщение из ваших сборщиков данных:

throw new CustomException(400, "A custom error message");

Теперь есть другой способ решения этой проблемы.

Предполагая, что вы работаете с веб-приложением, вы можете возвращать ошибки (и данные в этом отношении) в любом формате, который вы хотите. Хотя это немного неловко, на мой взгляд. Клиенты GraphQL, такие как Apollo, придерживаются спецификации, так почему вы хотите вернуть ответ в любом другом формате? Но в любом случае существует множество различных требований.

Как только вы получите ExecutionResult, вы можете создать карту или объект в любом формате, который хотите, сериализовать его как JSON и вернуть по HTTP.

Map<String, Object> result = new HashMap<>();

result.put("data", executionResult.getData());

List<Map<String, Object>> errors = executionResult.getErrors()
        .stream()
        .map(error -> {
            Map<String, Object> errorMap = new HashMap<>();

            errorMap.put("errorMessage", error.getMessage());
            errorMap.put("errorCode", 404); // get the code somehow from the error object

            return errorMap;
        })
        .collect(toList());

result.put("errors", errors);

// Serialize "result" and return that.

Но опять же, иметь ответ, который не соответствует спецификации, не имеет смысла в большинстве случаев.

...