You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Kickstart GraphQL实现自定义异常响应格式

自定义Kickstart GraphQL错误响应格式

问题场景

使用Kickstart GraphQL库时,遇到两种错误场景需要统一转换为自定义格式:

  1. 输入参数超出Int范围时,框架返回默认验证错误
  2. Resolver中抛出CustomGraphQLError自定义异常时

当前默认错误响应(超出Int限制示例)

{
    "errors": [
        {
            "message": "Validation error of type WrongType: argument 'inputLocals.localId' with value 'IntValue{value=7777800}' is not a valid 'Int' - Expected value to be in the Integer range but it was '7777800' @ 'getLocals'",
            "locations": [
                {
                    "line": 1,
                    "column": 25
                }
            ],
            "extensions": {
                "classification": "ValidationError"
            }
        }
    ],
    "data": null
}

期望的自定义错误格式

{
    "code": "400",
    "reason": "BAD_REQUEST",
    "message": "/localId 7777800 is not less or equal to 99999",
    "status": "",
    "referenceError": ""
}

现有Resolver代码

@Override
public OutputLocals getLocals(InputLocals inputLocals) throws CustomGraphQLError {
    int localId = inputLocals.localId();
    if (localId > 99999) {
        throw new CustomGraphQLError("fields matching the JSON above");
    }
    // 剩余业务代码
}

解决方案

1. 重构自定义异常类CustomGraphQLError

让异常携带字段和值信息,方便后续生成标准化错误消息:

public class CustomGraphQLError extends RuntimeException {
    private final String field;
    private final String value;

    public CustomGraphQLError(String field, String value) {
        super(String.format("/%s %s is not less or equal to 99999", field, value));
        this.field = field;
        this.value = value;
    }

    public String getField() { return field; }
    public String getValue() { return value; }
}

同时修改Resolver中抛出异常的逻辑:

if (localId > 99999) {
    throw new CustomGraphQLError("localId", String.valueOf(localId));
}

2. 实现自定义错误处理器

创建CustomGraphQLErrorHandler继承默认处理器,统一处理两类错误:

import graphql.ExceptionWhileDataFetching;
import graphql.GraphQLError;
import graphql.validation.ValidationError;
import org.springframework.graphql.execution.DefaultGraphQLErrorHandler;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Component
public class CustomGraphQLErrorHandler extends DefaultGraphQLErrorHandler {

    @Override
    public List<GraphQLError> processErrors(List<GraphQLError> errors) {
        return errors.stream()
                .map(this::convertToCustomFormat)
                .collect(Collectors.toList());
    }

    private GraphQLError convertToCustomFormat(GraphQLError error) {
        // 处理Resolver抛出的自定义异常
        if (error instanceof ExceptionWhileDataFetching) {
            Throwable cause = ((ExceptionWhileDataFetching) error).getException();
            if (cause instanceof CustomGraphQLError) {
                return new CustomErrorResponse(
                        "400",
                        "BAD_REQUEST",
                        cause.getMessage(),
                        "",
                        ""
                );
            }
        }

        // 处理框架默认的参数验证错误
        if (error instanceof ValidationError) {
            String msg = ((ValidationError) error).getMessage();
            String field = extractField(msg);
            String value = extractValue(msg);
            String customMsg = String.format("/%s %s is not less or equal to 99999", field, value);
            return new CustomErrorResponse(
                    "400",
                    "BAD_REQUEST",
                    customMsg,
                    "",
                    ""
            );
        }

        // 其他错误保持默认处理
        return error;
    }

    // 从默认错误消息中提取字段名
    private String extractField(String message) {
        int start = message.indexOf("argument '") + 10;
        int end = message.indexOf("' with value");
        String fullField = message.substring(start, end);
        return fullField.substring(fullField.lastIndexOf(".") + 1);
    }

    // 从默认错误消息中提取参数值
    private String extractValue(String message) {
        int start = message.indexOf("value '") + 7;
        int end = message.indexOf("}' is not a valid");
        return message.substring(start, end);
    }
}

3. 定义自定义错误响应类

实现GraphQLError接口,确保输出格式完全符合预期:

import graphql.GraphQLError;
import graphql.language.SourceLocation;

import java.util.List;
import java.util.Map;

public class CustomErrorResponse implements GraphQLError {
    private final String code;
    private final String reason;
    private final String message;
    private final String status;
    private final String referenceError;

    public CustomErrorResponse(String code, String reason, String message, String status, String referenceError) {
        this.code = code;
        this.reason = reason;
        this.message = message;
        this.status = status;
        this.referenceError = referenceError;
    }

    @Override
    public String getMessage() {
        return message;
    }

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

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

    // 重写该方法,直接返回自定义格式的Map
    @Override
    public Map<String, Object> toSpecification() {
        return Map.of(
                "code", code,
                "reason", reason,
                "message", message,
                "status", status,
                "referenceError", referenceError
        );
    }
}

4. 配置处理器(Spring环境)

如果是Spring Boot项目,CustomGraphQLErrorHandler上的@Component注解会让Spring自动扫描并注册;如果是手动配置,可添加如下配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.execution.GraphQLErrorHandler;

@Configuration
public class GraphQLConfig {
    @Bean
    public GraphQLErrorHandler graphQLErrorHandler() {
        return new CustomGraphQLErrorHandler();
    }
}

内容的提问来源于stack exchange,提问作者cdelrey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 22:51:06