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

Spring Webflux+GraphQL拦截器异常未进入ControllerAdvice处理

问题描述

使用Spring Webflux(3.2.2)和Spring GraphQL(3.2.2)框架,实现了WebGraphQlInterceptor用于检查请求头,当未找到指定请求头时抛出CustomException。但自定义的@ControllerAdvice类中,处理CustomException的@GraphQlExceptionHandler方法并未被触发,异常没有按预期处理。

相关代码

GraphQL异常处理器

@ControllerAdvice
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GraphQLExceptionHandler {

    @GraphQlExceptionHandler(CustomException.class)
    public GraphQLError handleCustomException(CustomException e) {
        return GraphQLError.newError()
                .errorType(resolve(e.errorCode))
                .message(e.getMessage())
                .build();
    }

    @GraphQlExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public GraphQLError handleException(Exception e) {
        return GraphQLError.newError()
                .errorType(ErrorType.INTERNAL_ERROR)
                .message(e.getMessage())
                .build();
    }

    @ExceptionHandler(CustomException.class)
    public final Mono<ResponseEntity<Map<String, Object>>> handleCustomExceptions(CustomException ex,
                                                                                 ServerWebExchange exchange) {
        log.info("Application Exception occurred : {} ", ex.getMessage());
        ErrorDetails errorDetails = new ErrorDetails(ex.errorCode, ex.getMessage(), ex,
                exchange.getRequest().getPath().value());
        return Mono.just(ResponseEntity.status(HttpStatus.resolve(ex.errorCode)).body(formatError(errorDetails)));
    }

    @ResponseStatus(INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public final Mono<ResponseEntity<Map<String, Object>>> handleExceptions(Exception ex, ServerWebExchange exchange) {
        log.error("Exception occurred : {0}", ex);
        ErrorDetails errorDetails = new ErrorDetails(INTERNAL_SERVER_ERROR.value(),
                "Something went wrong, please try again after sometime", ex, exchange.getRequest().getPath().value());
        return Mono.just(ResponseEntity.status(INTERNAL_SERVER_ERROR).body(formatError(errorDetails)));
    }

    private Map<String, Object> formatError(ErrorDetails errorDetails) {
        Map<String, Object> error = new HashMap<>();
        error.put("errors", errorDetails);
        return error;
    }

    private ErrorType resolve(int errorCode) {
        return switch (errorCode) {
            case 401 -> ErrorType.UNAUTHORIZED;
            case 403 -> ErrorType.FORBIDDEN;
            case 404 -> ErrorType.NOT_FOUND;
            default -> ErrorType.INTERNAL_ERROR;
        };
    }
}

请求头拦截器

@Component
public class RequestHeaderInterceptor implements WebGraphQlInterceptor {

    public static final String AUTHORIZATION_HEADER_KEY = "Authorization";

    @NotNull
    @Override
    public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
        String value = request.getHeaders().getFirst(AUTHORIZATION_HEADER_KEY);
        if (value == null) {
            throw new CustomException(ErrorMessage.AUTH_ID_NOT_FOUND);
        }
        request.configureExecutionInput((executionInput, builder) ->
                builder.graphQLContext(Collections.singletonMap("authHeader", value)).build());
        return chain.next(request);
    }
}
问题原因与解决方法

核心问题:拦截器中异常抛出方式错误

WebGraphQlInterceptor运行在WebFlux异步非阻塞的请求链路中,直接同步抛出CustomException会被WebFlux的全局异常处理器(即你代码中的@ExceptionHandler方法)捕获,而非Spring GraphQL的@GraphQlExceptionHandler。只有通过Mono.error()抛出异常,才能让异常进入GraphQL的处理链路,被对应的异常处理器处理。

修改后的拦截器代码

@Component
public class RequestHeaderInterceptor implements WebGraphQlInterceptor {

    public static final String AUTHORIZATION_HEADER_KEY = "Authorization";

    @NotNull
    @Override
    public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
        String value = request.getHeaders().getFirst(AUTHORIZATION_HEADER_KEY);
        if (value == null) {
            // 改为返回Mono.error,将异常注入异步链路
            return Mono.error(new CustomException(ErrorMessage.AUTH_ID_NOT_FOUND));
        }
        request.configureExecutionInput((executionInput, builder) ->
                builder.graphQLContext(Collections.singletonMap("authHeader", value)).build());
        return chain.next(request);
    }
}

额外注意事项

  1. 确保CustomException是运行时异常:自定义异常需继承RuntimeException,避免在异步链路中需要额外处理检查型异常。
  2. GraphQL异常处理器的@ResponseStatus无效:GraphQL规范推荐响应HTTP状态码为200,错误信息放在响应体的errors数组中。如果需要修改HTTP状态码,建议通过WebFilter或在拦截器中操作ServerWebExchange实现,而非依赖@ResponseStatus。
  3. @Order注解的作用:当前GraphQLExceptionHandler已设置@Order(Ordered.HIGHEST_PRECEDENCE),确保它优先于其他异常处理器执行,无需调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 05:18:12