Spring Boot中如何处理GraphQL查询校验错误并自定义错误消息
原因说明
你实现的DataFetcherExceptionResolver仅能捕获数据获取阶段抛出的异常,也就是@QueryMapping标注的控制器方法执行、字段值序列化/反序列化过程中产生的异常。
而Schema中标记为非空(String!)的参数缺失错误,属于GraphQL请求流程中验证阶段抛出的ValidationError,该阶段执行时机早于DataFetcher调用,因此不会进入你编写的异常处理逻辑。
实现方案
你可以通过以下两种方式拦截这类验证阶段的错误,自定义返回提示:
方案一:扩展默认错误属性处理器
继承Spring GraphQL内置的DefaultGraphQLErrorAttributes,重写错误属性组装逻辑,针对参数缺失的验证错误自定义消息:
import graphql.GraphQLError; import graphql.validation.ValidationError; import graphql.validation.ValidationErrorType; import org.springframework.graphql.execution.DefaultGraphQLErrorAttributes; import org.springframework.graphql.execution.ErrorAttributeOptions; import org.springframework.stereotype.Component; import java.util.Map; @Component public class CustomGraphQLErrorAttributes extends DefaultGraphQLErrorAttributes { @Override public Map<String, Object> getErrorAttributes(GraphQLError error, ErrorAttributeOptions options) { Map<String, Object> attributes = super.getErrorAttributes(error, options); if (error instanceof ValidationError ve && ve.getValidationErrorType() == ValidationErrorType.MissingFieldArgument) { // 替换为自定义提示内容 attributes.put("message", "参数校验失败:必填参数未传入"); // 可按需扩展自定义错误码等字段 attributes.put("errorCode", "PARAM_REQUIRED_MISSING"); } return attributes; } }
该实现会拦截GraphQL全流程(验证、数据获取、序列化)产生的所有错误,你可以根据错误类型灵活定制返回结构。
方案二:自定义执行结果处理器
如果需要更灵活的结果控制,可以实现ExecutionResultHandler接口,在请求结果返回前统一遍历错误列表,替换需要修改的错误内容:
import graphql.ExecutionResult; import graphql.GraphQLError; import graphql.validation.ValidationError; import graphql.validation.ValidationErrorType; import org.springframework.graphql.ExecutionResultHandler; import org.springframework.graphql.support.DefaultExecutionGraphQlResponse; import org.springframework.stereotype.Component; import java.util.List; import java.util.stream.Collectors; @Component public class CustomExecutionResultHandler implements ExecutionResultHandler { @Override public Object handleExecutionResult(DefaultExecutionGraphQlResponse response) { ExecutionResult originResult = response.getExecutionResult(); List<GraphQLError> modifiedErrors = originResult.getErrors().stream() .map(error -> { if (error instanceof ValidationError ve && ve.getValidationErrorType() == ValidationErrorType.MissingFieldArgument) { // 构造自定义错误替换原始错误 return error.transform(builder -> builder.message("请求错误:缺少必填参数")); } return error; }).collect(Collectors.toList()); return originResult.transform(builder -> builder.errors(modifiedErrors)); } }
补充说明
你之前实现的DataFetcherExceptionResolver不需要删除,它依然可以正常处理数据获取阶段的Coercing类异常、业务异常,和上述两种方案完全不冲突。
如果后续需要处理Java Bean参数上@Valid注解触发的校验错误,也可以在上述错误处理逻辑中扩展对应错误类型的判断,统一改写提示消息。
内容的提问来源于stack exchange,提问作者Ivan Polovyi
相关产品推荐
相关产品推荐

