1. 概述
本教程将深入探讨GraphQL中的错误处理机制。首先分析GraphQL规范对错误响应的要求,然后通过Spring Boot实战演示错误处理的最佳实践。对于有经验的开发者来说,理解错误处理是构建健壮GraphQL服务的关键。
2. GraphQL规范中的响应结构
根据GraphQL规范,每个请求必须返回结构化良好的响应。这种响应由成功或失败操作产生的data
或errors
映射组成,还可能包含部分成功结果和字段错误。
响应映射的核心组件是errors
、data
和extensions
。
errors
部分描述操作过程中的任何失败。若无错误发生,响应中必须不包含此部分data
部分包含成功执行的操作结果:- 查询操作时:查询根操作类型的对象
- 变更操作时:变更根操作类型的对象
- 若操作因信息缺失、验证错误或语法错误而失败,响应中必须不包含此部分
- 若操作执行失败,此部分必须为
null
extensions
是可选的映射对象,允许实现者按需添加自定义内容,格式无限制
关键规则:若响应不含data
,则必须包含至少一个错误的errors
部分。例如违反唯一约束时的错误响应:
mutation {
addVehicle(vin: "NDXT155NDFTV59834", year: 2021, make: "Toyota", model: "Camry", trim: "XLE",
location: {zipcode: "75024", city: "Dallas", state: "TX"}) {
vin
year
make
model
trim
}
}
{
"data": null,
"errors": [
{
"errorType": "DataFetchingException",
"locations": [
{
"line": 2,
"column": 5,
"sourceName": null
}
],
"message": "Failed to add vehicle. Vehicle with vin NDXT155NDFTV59834 already present.",
"path": [
"addVehicle"
],
"extensions": {
"vin": "NDXT155NDFTV59834"
}
}
]
}
3. GraphQL规范中的错误响应组件
响应中的errors
部分是非空错误列表,每个错误都是映射对象。
3.1. 请求错误
这类错误发生在操作执行前,通常由请求本身的问题引起,包括:
- 请求数据解析失败
- 请求文档验证错误
- 不支持的操作
- 无效的请求值
发生请求错误时,操作尚未开始执行,响应中必须不包含data
部分。例如语法错误示例:
query {
searchByVin(vin: "error) {
vin
year
make
model
trim
}
}
错误响应(缺少引号导致):
{
"data": null,
"errors": [
{
"message": "Invalid Syntax",
"locations": [
{
"line": 5,
"column": 8,
"sourceName": null
}
],
"errorType": "InvalidSyntax",
"path": null,
"extensions": null
}
]
}
3.2. 字段错误
字段错误发生在操作执行期间,通常由以下原因引起:
- 值无法强制转换为预期类型
- 特定字段解析时发生内部错误
发生字段错误时,操作会继续执行并返回部分结果,响应中必须同时包含data
和errors
部分。例如查询非空字段但实际为空的情况:
query {
searchAll {
vin
year
make
model
trim
}
}
错误响应(trim字段被标记为非空但存在null值):
{
"data": {
"searchAll": [
null,
{
"vin": "JTKKU4B41C1023346",
"year": 2012,
"make": "Toyota",
"model": "Scion",
"trim": "Xd"
},
{
"vin": "1G1JC1444PZ215071",
"year": 2000,
"make": "Chevrolet",
"model": "CAVALIER VL",
"trim": "RS"
}
]
},
"errors": [
{
"message": "Cannot return null for non-nullable type: 'String' within parent 'Vehicle' (/searchAll[0]/trim)",
"path": [
"searchAll",
0,
"trim"
],
"errorType": "DataFetchingException",
"locations": null,
"extensions": null
}
]
}
3.3. 错误响应格式
每个错误必须包含以下关键元素:
必需字段:
message
:描述失败原因,帮助客户端开发者修正错误
可选字段:
locations
:错误关联的GraphQL文档位置列表"locations": [ { "line": 5, "column": 8 } ]
path
:从根元素到错误字段的路径"path": [ "searchAll", 0, "trim" ]
- 值为字段名(字符串)或列表索引(数字)
- 若字段有别名,路径中使用别名
3.4. 字段错误处理规则
处理字段错误时需遵循以下原则:
基础规则:
- 无论字段是否可空,错误都应视为字段返回
null
- 错误必须添加到
errors
列表
- 无论字段是否可空,错误都应视为字段返回
可空字段:
- 响应中字段值为
null
errors
中包含描述该字段的错误
- 响应中字段值为
非空字段:
- 错误由父字段处理
- 若父字段非空,错误会向上传播直到找到可空父字段或根元素
列表字段:
- 若列表包含非空类型且某个元素为
null
,整个列表解析为null
- 若包含该列表的父字段非空,错误同样向上传播
- 若列表包含非空类型且某个元素为
多重错误:
- 同一字段解析时即使出现多个错误,
errors
中只添加一个该字段的错误
- 同一字段解析时即使出现多个错误,
4. Spring Boot GraphQL库依赖
在Spring Boot应用中,我们使用以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
spring-boot-starter-graphql
:提供核心GraphQL功能spring-graphql-test
:用于GraphQL服务测试
5. Spring Boot GraphQL错误处理
本节聚焦Spring Boot应用内的GraphQL错误处理实现(非基础开发知识)。我们将通过车辆查询/变更示例展示不同错误处理方式。
5.1. 标准异常的GraphQL响应
首先创建自定义异常:
public class InvalidInputException extends RuntimeException {
public InvalidInputException(String message) {
super(message);
}
}
默认情况下,GraphQL引擎返回如下通用错误响应:
{
"errors": [
{
"message": "INTERNAL_ERROR for 2c69042a-e7e6-c0c7-03cf-6026b1bbe559",
"locations": [
{
"line": 2,
"column": 5
}
],
"path": [
"searchByLocation"
],
"extensions": {
"classification": "INTERNAL_ERROR"
}
}
],
"data": null
}
默认处理机制:
- 由
ExceptionResolversExceptionHandler
处理所有异常 - 实现
DataFetcherExceptionHandler
接口 - 支持注册多个
DataFetcherExceptionResolver
组件 - 未解析异常被归类为
INTERNAL_ERROR
(含执行ID和通用消息)
5.2. 自定义异常的GraphQL响应
创建更具体的异常类:
public class VehicleNotFoundException extends RuntimeException {
public VehicleNotFoundException(String message) {
super(message);
}
}
实现自定义异常解析器(推荐继承DataFetcherExceptionResolverAdapter
):
@Component
public class CustomExceptionResolver extends DataFetcherExceptionResolverAdapter {
@Override
protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) {
if (ex instanceof VehicleNotFoundException) {
return GraphqlErrorBuilder.newError()
.errorType(ErrorType.NOT_FOUND)
.message(ex.getMessage())
.path(env.getExecutionStepInfo().getPath())
.location(env.getField().getSourceLocation())
.build();
} else {
return null;
}
}
}
现在错误响应将包含明确分类:
{
"errors": [
{
"message": "Vehicle with vin: 123 not found.",
"locations": [
{
"line": 2,
"column": 5
}
],
"path": [
"searchByVin"
],
"extensions": {
"classification": "NOT_FOUND"
}
}
],
"data": {
"searchByVin": null
}
}
关键机制:
- 未解析异常以ERROR级别记录(含执行ID)
- 已解析异常以DEBUG级别记录
- 通过
GraphqlErrorBuilder
构建结构化错误
6. 总结
本文系统梳理了GraphQL错误处理的核心概念:
规范层面:
- 掌握
errors
/data
/extensions
响应结构 - 区分请求错误与字段错误处理逻辑
- 理解非空字段的错误传播机制
- 掌握
Spring Boot实现:
- 利用
DataFetcherExceptionResolver
自定义错误处理 - 通过
GraphqlErrorBuilder
构建结构化错误响应 - 合理使用错误分类(如
NOT_FOUND
/INTERNAL_ERROR
)
- 利用
实际开发中,建议为不同业务场景设计专用异常类,通过自定义解析器提供明确的错误分类和上下文信息,避免暴露内部实现细节。完整示例代码可在GitHub获取。