我正在graphgraphl应用程序中工作,无论它是否出现在servlet或服务中,我都必须在json中发送自定义错误对象/消息。
预期的错误响应
plot "< awk <data.asc 'end!=0 && NR<=end{print} /^start/{end=NR+1500}'" using 1:2 with lines
如果有人可以指导我达到上述要求,将会很有帮助。
答案 0 :(得分:5)
GraphQL specification为响应中的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
条目中以任何格式添加数据。因此,您可以在服务器端创建一个自定义Exception并最终得到一个类似于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上实现它,如the docs中所述。您可以创建一个覆盖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");
现在,有另一种解决方法。
假设您正在使用Web应用程序,则可以以所需的任何格式返回错误(以及相关数据)。虽然在我看来这有点尴尬。 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.
但同样,在大多数情况下,做出不符合规范的回应是没有道理的。
答案 1 :(得分:2)
另一个发布的答案对我不起作用。 我通过创建以下类找到了解决方案:
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");
}
}