Spring Cloud Gateway 异常处理与错误响应

摘要

本文深入探讨Spring Cloud Gateway的异常处理机制,包括全局异常处理器、自定义错误响应、异常监控等内容。通过详细的代码示例和架构分析,帮助开发者构建健壮的网关异常处理体系。

1. 异常处理概述

1.1 网关异常类型

在Spring Cloud Gateway中,常见的异常类型包括:

  • 路由异常:路由匹配失败、目标服务不可用
  • 认证异常:认证失败、令牌过期
  • 限流异常:请求被限流
  • 系统异常:网络超时、服务降级

1.2 异常处理的重要性

完善的异常处理机制能够:

  • 提供友好的错误响应
  • 避免系统级错误暴露
  • 收集异常信息用于监控
  • 提升用户体验

2. Spring WebFlux 异常处理机制

2.1 WebExceptionHandler 接口

public interface WebExceptionHandler {
    /**
     * 处理Web交换异常
     */
    Mono<Void> handle(ServerWebExchange exchange, Throwable ex);
}

2.2 默认异常处理器

Spring Cloud Gateway提供了默认的异常处理器,但通常需要自定义以满足业务需求。

3. 自定义全局异常处理器

3.1 自定义异常处理器实现

/**
 * 自定义全局异常处理器
 * 处理网关层的各种异常情况
 */
@Slf4j
@Component
@Order(-2)
public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
    
    public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes,
                                         ApplicationContext applicationContext,
                                         ServerCodecConfigurer serverCodecConfigurer) {
        super(errorAttributes, new WebProperties.Resources(), applicationContext);
        super.setMessageWriters(serverCodecConfigurer.getWriters());
        super.setMessageReaders(serverCodecConfigurer.getReaders());
    }
    
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }
    
    /**
     * 渲染错误响应
     */
    private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        Map<String, Object> errorPropertiesMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
        Throwable error = getError(request);
        
        // 根据不同异常类型返回相应的错误响应
        ErrorResponseInfo errorResponseInfo = buildErrorResponse(error, errorPropertiesMap);
        
        log.error("网关异常: {}", errorResponseInfo.getMessage(), error);
        
        return ServerResponse.status(errorResponseInfo.getStatus())
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponseInfo));
    }
    
    /**
     * 构建错误响应信息
     */
    private ErrorResponseInfo buildErrorResponse(Throwable error, Map<String, Object> errorPropertiesMap) {
        ErrorResponseInfo responseInfo = new ErrorResponseInfo();
        
        if (error instanceofResponseStatusException) {
            ResponseStatusException statusException = (ResponseStatusException) error;
            responseInfo.setStatus(statusException.getStatus().value());
            responseInfo.setMessage(statusException.getReason());
        } else if (error instanceof SocketException) {
            responseInfo.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
            responseInfo.setMessage("目标服务不可用");
        } else if (error instanceof ConnectTimeoutException) {
            responseInfo.setStatus(HttpStatus.GATEWAY_TIMEOUT.value());
            responseInfo.setMessage("连接超时");
        } else if (error instanceof ReadTimeoutException) {
            responseInfo.setStatus(HttpStatus.GATEWAY_TIMEOUT.value());
            responseInfo.setMessage("读取超时");
        } else if (error instanceof TooManyRequestsException) {
            responseInfo.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            responseInfo.setMessage("请求过于频繁,请稍后再试");
        } else {
            responseInfo.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            responseInfo.setMessage("系统内部错误");
        }
        
        // 设置错误代码和时间戳
        responseInfo.setCode(responseInfo.getStatus());
        responseInfo.setTimestamp(System.currentTimeMillis());
        
        // 添加请求ID用于追踪
        responseInfo.setRequestId(UUID.randomUUID().toString());
        
        return responseInfo;
    }
    
    /**
     * 错误响应信息类
     */
    @Data
    public static class ErrorResponseInfo {
        private int code;
        private String message;
        private int status;
        private long timestamp;
        private String requestId;
        private Map<String, Object> details;
    }
}

3.2 异常处理器自动配置

/**
 * 异常处理器自动配置
 * 自动注册自定义异常处理器
 */
@Configuration
@EnableConfigurationProperties(WebProperties.class)
public class ExceptionAutoConfiguration {
    
    private final WebProperties webProperties;
    
    public ExceptionAutoConfiguration(WebProperties webProperties) {
        this.webProperties = webProperties;
    }
    
    @Bean
    @ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, 
                             search = SearchStrategy.CURRENT)
    public ErrorWebExceptionHandler errorWebExceptionHandler(
            ErrorAttributes errorAttributes,
            WebProperties webProperties,
            ServerCodecConfigurer serverCodecConfigurer,
            ApplicationContext applicationContext) {
        
        CustomErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(
                errorAttributes,
                applicationContext,
                serverCodecConfigurer);
        
        exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
        
        return exceptionHandler;
    }
    
    @Bean
    @ConditionalOnMissingBean
    public ErrorAttributes errorAttributes() {
        return new GatewayErrorAttributes();
    }
}

/**
 * 自定义错误属性
 * 控制错误响应中包含的属性
 */
@Slf4j
public class GatewayErrorAttributes implements ErrorAttributes {
    
    private static final String ERROR_ATTRIBUTE = 
        GatewayErrorAttributes.class.getName() + ".ERROR";
    
    @Override
    public void storeErrorInformation(Throwable error, ServerWebExchange exchange) {
        exchange.getAttributes().put(ERROR_ATTRIBUTE, error);
    }
    
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, 
                                                 ErrorAttributeOptions options) {
        Map<String, Object> errorMap = new HashMap<>();
        
        // 获取原始异常
        Throwable error = getError(request);
        if (error != null) {
            errorMap.put("error", error.getClass().getSimpleName());
            errorMap.put("message", error.getMessage());
            
            // 记录异常日志
            log.error("请求处理异常", error);
        }
        
        // 添加请求信息
        errorMap.put("timestamp", System.currentTimeMillis());
        errorMap.put("path", request.path());
        
        return errorMap;
    }
    
    @Override
    public Throwable getError(ServerRequest request) {
        return (Throwable) request.attribute(ERROR_ATTRIBUTE)
            .orElseThrow(() -> new IllegalStateException(
                "Missing exception attribute in ServerWebExchange"));
    }
}

4. 网关特定异常处理

4.1 网关异常处理切面

/**
 * 网关异常处理切面
 * 捕获和处理网关层的特定异常
 */
@Slf4j
@Component
public class GateWayExceptionHandlerAdvice {
    
    /**
     * 处理网关路由异常
     */
    @ExceptionHandler(NoSuchElementException.class)
    public Mono<ServerResponse> handleNoSuchElementException(ServerRequest request, 
                                                           NoSuchElementException ex) {
        log.error("路由未找到异常: {}", ex.getMessage(), ex);
        
        ErrorResponseInfo errorResponse = createErrorResponse(
            HttpStatus.NOT_FOUND.value(), 
            "请求的路由不存在"
        );
        
        return ServerResponse.status(HttpStatus.NOT_FOUND)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 处理路由定义异常
     */
    @ExceptionHandler(RouteDefinitionException.class)
    public Mono<ServerResponse> handleRouteDefinitionException(ServerRequest request, 
                                                              RouteDefinitionException ex) {
        log.error("路由定义异常: {}", ex.getMessage(), ex);
        
        ErrorResponseInfo errorResponse = createErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR.value(), 
            "路由配置错误"
        );
        
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 处理超时异常
     */
    @ExceptionHandler({TimeoutException.class, ReadTimeoutException.class, ConnectTimeoutException.class})
    public Mono<ServerResponse> handleTimeoutException(ServerRequest request, Exception ex) {
        log.error("请求超时异常: {}", ex.getMessage(), ex);
        
        ErrorResponseInfo errorResponse = createErrorResponse(
            HttpStatus.GATEWAY_TIMEOUT.value(), 
            "请求超时,请稍后重试"
        );
        
        return ServerResponse.status(HttpStatus.GATEWAY_TIMEOUT)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 处理连接异常
     */
    @ExceptionHandler({SocketException.class, ConnectException.class})
    public Mono<ServerResponse> handleConnectionException(ServerRequest request, Exception ex) {
        log.error("连接异常: {}", ex.getMessage(), ex);
        
        ErrorResponseInfo errorResponse = createErrorResponse(
            HttpStatus.SERVICE_UNAVAILABLE.value(), 
            "目标服务不可用"
        );
        
        return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 处理限流异常
     */
    @ExceptionHandler(TooManyRequestsException.class)
    public Mono<ServerResponse> handleTooManyRequestsException(ServerRequest request, 
                                                              TooManyRequestsException ex) {
        log.warn("请求过于频繁: {}", ex.getMessage());
        
        ErrorResponseInfo errorResponse = createErrorResponse(
            HttpStatus.TOO_MANY_REQUESTS.value(), 
            "请求过于频繁,请稍后再试"
        );
        
        ServerResponse response = ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
            .header("Retry-After", "60") // 建议客户端60秒后重试
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
        
        return response;
    }
    
    /**
     * 处理认证异常
     */
    @ExceptionHandler(UnauthorizedException.class)
    public Mono<ServerResponse> handleUnauthorizedException(ServerRequest request, 
                                                           UnauthorizedException ex) {
        log.warn("认证失败: {}", ex.getMessage());
        
        ErrorResponseInfo errorResponse = createErrorResponse(
            HttpStatus.UNAUTHORIZED.value(), 
            "认证失败,请重新登录"
        );
        
        return ServerResponse.status(HttpStatus.UNAUTHORIZED)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 处理通用异常
     */
    @ExceptionHandler(Exception.class)
    public Mono<ServerResponse> handleGenericException(ServerRequest request, Exception ex) {
        log.error("通用异常: {}", ex.getMessage(), ex);
        
        ErrorResponseInfo errorResponse = createErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR.value(), 
            "系统内部错误"
        );
        
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 创建错误响应
     */
    private ErrorResponseInfo createErrorResponse(int code, String message) {
        ErrorResponseInfo response = new ErrorResponseInfo();
        response.setCode(code);
        response.setMessage(message);
        response.setStatus(code);
        response.setTimestamp(System.currentTimeMillis());
        response.setRequestId(UUID.randomUUID().toString());
        return response;
    }
    
    /**
     * 错误响应信息类
     */
    @Data
    public static class ErrorResponseInfo {
        private int code;
        private String message;
        private int status;
        private long timestamp;
        private String requestId;
        private Map<String, Object> details;
    }
}

5. 异常响应格式统一

5.1 统一响应格式

/**
 * 统一响应格式
 * 定义API响应的标准格式
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
    private int code;
    private String message;
    private T data;
    private long timestamp;
    private String requestId;
    
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage("success");
        result.setData(data);
        result.setTimestamp(System.currentTimeMillis());
        result.setRequestId(UUID.randomUUID().toString());
        return result;
    }
    
    public static <T> Result<T> success(String message, T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage(message);
        result.setData(data);
        result.setTimestamp(System.currentTimeMillis());
        result.setRequestId(UUID.randomUUID().toString());
        return result;
    }
    
    public static <T> Result<T> error(int code, String message) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMessage(message);
        result.setTimestamp(System.currentTimeMillis());
        result.setRequestId(UUID.randomUUID().toString());
        return result;
    }
    
    public static <T> Result<T> error(String message) {
        return error(500, message);
    }
    
    public static <T> Result<T> error(HttpStatus status, String message) {
        return error(status.value(), message);
    }
}

5.2 异常响应格式

/**
 * 异常响应格式
 * 专门用于异常情况的响应格式
 */
@Data
public class ErrorResponse {
    private int code;
    private String message;
    private String error;
    private long timestamp;
    private String path;
    private String requestId;
    
    public static ErrorResponse of(HttpStatus status, String message, String path) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setCode(status.value());
        errorResponse.setMessage(message);
        errorResponse.setError(status.getReasonPhrase());
        errorResponse.setTimestamp(System.currentTimeMillis());
        errorResponse.setPath(path);
        errorResponse.setRequestId(UUID.randomUUID().toString());
        return errorResponse;
    }
}

6. 异常监控与日志

6.1 异常监控过滤器

/**
 * 异常监控过滤器
 * 记录和监控异常事件
 */
@Component
public class ExceptionMonitorFilter implements GlobalFilter, Ordered {
    
    private static final Logger exceptionLogger = LoggerFactory.getLogger("EXCEPTION_LOGGER");
    private final MeterRegistry meterRegistry;
    
    public ExceptionMonitorFilter(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        long startTime = System.currentTimeMillis();
        
        return chain.filter(exchange)
            .doOnError(throwable -> {
                // 记录异常信息
                recordException(exchange, throwable, startTime);
            })
            .doOnSuccess(signal -> {
                // 记录成功请求的响应时间
                long duration = System.currentTimeMillis() - startTime;
                recordSuccessRequest(exchange, duration);
            });
    }
    
    private void recordException(ServerWebExchange exchange, Throwable throwable, long startTime) {
        long duration = System.currentTimeMillis() - startTime;
        String path = exchange.getRequest().getURI().getPath();
        String method = exchange.getRequest().getMethodValue();
        String clientIp = getClientIp(exchange);
        
        // 记录异常日志
        exceptionLogger.error("异常请求 - path: {}, method: {}, clientIp: {}, duration: {}ms, error: {}", 
            path, method, clientIp, duration, throwable.getMessage(), throwable);
        
        // 更新监控指标
        if (meterRegistry != null) {
            meterRegistry.counter("gateway.exceptions.total", 
                Tags.of(
                    "exception", throwable.getClass().getSimpleName(),
                    "path", path,
                    "method", method
                )
            ).increment();
            
            meterRegistry.timer("gateway.request.duration", 
                Tags.of("status", "error", "path", path, "method", method)
            ).record(duration, TimeUnit.MILLISECONDS);
        }
    }
    
    private void recordSuccessRequest(ServerWebExchange exchange, long duration) {
        String path = exchange.getRequest().getURI().getPath();
        String method = exchange.getRequest().getMethodValue();
        
        if (meterRegistry != null) {
            meterRegistry.timer("gateway.request.duration", 
                Tags.of("status", "success", "path", path, "method", method)
            ).record(duration, TimeUnit.MILLISECONDS);
        }
    }
    
    private String getClientIp(ServerWebExchange exchange) {
        String xForwardedFor = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = exchange.getRequest().getHeaders().getFirst("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
        return remoteAddress != null ? remoteAddress.getAddress().getHostAddress() : "unknown";
    }
    
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

7. 异常处理架构

客户端请求

网关入口

业务过滤器

认证过滤器

限流过滤器

路由处理

请求处理成功?

正常响应

异常捕获

异常类型判断

路由异常

认证异常

限流异常

超时异常

其他异常

返回404

返回401

返回429

返回504

返回500

统一错误响应

返回给客户端

8. 特定异常处理

8.1 路由异常处理

/**
 * 路由异常处理器
 * 专门处理路由相关的异常
 */
@Component
public class RouteExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(RouteExceptionHandler.class);
    
    /**
     * 处理路由不可用异常
     */
    public Mono<ServerResponse> handleRouteUnavailable(ServerWebExchange exchange, 
                                                      String routeId, String serviceId) {
        logger.error("路由不可用 - routeId: {}, serviceId: {}", routeId, serviceId);
        
        ErrorResponse errorResponse = ErrorResponse.of(
            HttpStatus.SERVICE_UNAVAILABLE, 
            "目标服务暂时不可用,请稍后重试", 
            exchange.getRequest().getURI().getPath()
        );
        
        errorResponse.setError("Route Unavailable");
        
        return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 处理路由配置错误
     */
    public Mono<ServerResponse> handleRouteConfigurationError(ServerWebExchange exchange, 
                                                             String errorMessage) {
        logger.error("路由配置错误: {}", errorMessage);
        
        ErrorResponse errorResponse = ErrorResponse.of(
            HttpStatus.INTERNAL_SERVER_ERROR, 
            "网关路由配置错误", 
            exchange.getRequest().getURI().getPath()
        );
        
        errorResponse.setError("Route Configuration Error");
        
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
}

8.2 降级异常处理

/**
 * 降级异常处理器
 * 处理服务降级情况下的异常
 */
@Component
public class FallbackExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(FallbackExceptionHandler.class);
    
    /**
     * 服务降级响应
     */
    public Mono<ServerResponse> handleServiceFallback(ServerWebExchange exchange) {
        logger.warn("服务降级 - 返回默认响应");
        
        // 返回降级响应
        String fallbackResponse = "{\"code\": 200, \"message\": \"服务暂时不可用,已启用降级策略\", \"data\": null}";
        
        return ServerResponse.status(HttpStatus.OK)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(fallbackResponse));
    }
    
    /**
     * 熔断异常处理
     */
    public Mono<ServerResponse> handleCircuitBreakerOpen(ServerWebExchange exchange) {
        logger.warn("熔断器开启 - 拒绝请求");
        
        ErrorResponse errorResponse = ErrorResponse.of(
            HttpStatus.SERVICE_UNAVAILABLE, 
            "服务暂时不可用,熔断器已开启", 
            exchange.getRequest().getURI().getPath()
        );
        
        errorResponse.setError("Circuit Breaker Open");
        
        return ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
}

9. 异常处理测试

9.1 异常处理单元测试

@SpringBootTest
@AutoConfigureTestDatabase
public class ExceptionHandlerTest {
    
    @Autowired
    private WebTestClient webTestClient;
    
    @Test
    public void testRouteNotFoundException() {
        // 测试路由不存在的情况
        webTestClient.get()
            .uri("/nonexistent/path")
            .exchange()
            .expectStatus().isNotFound()
            .expectBody()
            .jsonPath("$.code").isEqualTo(404)
            .jsonPath("$.message").isEqualTo("请求的路由不存在");
    }
    
    @Test
    public void testTimeoutException() {
        // 测试超时异常
        webTestClient.get()
            .uri("/api/timeout")
            .exchange()
            .expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT)
            .expectBody()
            .jsonPath("$.code").isEqualTo(504)
            .jsonPath("$.message").isEqualTo("请求超时,请稍后重试");
    }
    
    @Test
    public void testTooManyRequestsException() {
        // 测试限流异常
        webTestClient.get()
            .uri("/api/rate-limited")
            .exchange()
            .expectStatus().isEqualTo(HttpStatus.TOO_MANY_REQUESTS)
            .expectBody()
            .jsonPath("$.code").isEqualTo(429)
            .jsonPath("$.message").isEqualTo("请求过于频繁,请稍后再试");
    }
}

10. 异常处理最佳实践

10.1 异常分类处理

/**
 * 异常分类处理器
 * 根据异常类型进行分类处理
 */
@Component
public class ClassifiedExceptionHandler {
    
    /**
     * 客户端错误处理 (4xx)
     */
    public Mono<ServerResponse> handleClientError(HttpStatus status, String message, 
                                                 ServerWebExchange exchange) {
        ErrorResponse errorResponse = ErrorResponse.of(status, message, 
            exchange.getRequest().getURI().getPath());
        errorResponse.setError("Client Error");
        
        return ServerResponse.status(status)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 服务端错误处理 (5xx)
     */
    public Mono<ServerResponse> handleServerError(HttpStatus status, String message, 
                                                 ServerWebExchange exchange) {
        ErrorResponse errorResponse = ErrorResponse.of(status, message, 
            exchange.getRequest().getURI().getPath());
        errorResponse.setError("Server Error");
        
        // 记录严重错误日志
        log.error("服务端错误 - status: {}, message: {}, path: {}", 
            status, message, exchange.getRequest().getURI().getPath());
        
        return ServerResponse.status(status)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
    
    /**
     * 系统错误处理
     */
    public Mono<ServerResponse> handleSystemError(String message, ServerWebExchange exchange) {
        ErrorResponse errorResponse = ErrorResponse.of(
            HttpStatus.INTERNAL_SERVER_ERROR, 
            "系统内部错误,请联系管理员", 
            exchange.getRequest().getURI().getPath()
        );
        errorResponse.setError("System Error");
        
        // 记录系统错误日志
        log.error("系统错误: {}", message);
        
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorResponse));
    }
}

10.2 异常响应模板

/**
 * 异常响应模板
 * 提供标准化的异常响应模板
 */
@Component
public class ExceptionResponseTemplate {
    
    private final Map<HttpStatus, String> responseTemplates;
    
    public ExceptionResponseTemplate() {
        responseTemplates = new HashMap<>();
        responseTemplates.put(HttpStatus.NOT_FOUND, 
            "{\"code\": %d, \"message\": \"请求的资源不存在\", \"error\": \"Not Found\", \"timestamp\": %d, \"path\": \"%s\"}");
        responseTemplates.put(HttpStatus.UNAUTHORIZED, 
            "{\"code\": %d, \"message\": \"认证失败,请重新登录\", \"error\": \"Unauthorized\", \"timestamp\": %d, \"path\": \"%s\"}");
        responseTemplates.put(HttpStatus.FORBIDDEN, 
            "{\"code\": %d, \"message\": \"权限不足,禁止访问\", \"error\": \"Forbidden\", \"timestamp\": %d, \"path\": \"%s\"}");
        responseTemplates.put(HttpStatus.TOO_MANY_REQUESTS, 
            "{\"code\": %d, \"message\": \"请求过于频繁,请稍后再试\", \"error\": \"Too Many Requests\", \"timestamp\": %d, \"path\": \"%s\"}");
        responseTemplates.put(HttpStatus.GATEWAY_TIMEOUT, 
            "{\"code\": %d, \"message\": \"请求超时,请稍后重试\", \"error\": \"Gateway Timeout\", \"timestamp\": %d, \"path\": \"%s\"}");
    }
    
    /**
     * 获取异常响应模板
     */
    public String getTemplate(HttpStatus status) {
        return responseTemplates.getOrDefault(status, 
            "{\"code\": %d, \"message\": \"%s\", \"error\": \"Internal Server Error\", \"timestamp\": %d, \"path\": \"%s\"}");
    }
}

11. 异常处理配置

11.1 配置示例

# 异常处理配置
gateway:
  exception:
    # 是否启用详细错误信息(生产环境建议关闭)
    show-details: false
    # 错误响应超时时间
    response-timeout: 5000
    # 是否记录异常堆栈信息
    log-stack-trace: true
    # 异常响应格式
    response-format: json

# 监控配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

# 日志配置
logging:
  level:
    com.springboot.cloud.gateway.exception: DEBUG
    EXCEPTION_LOGGER: INFO
    org.springframework.cloud.gateway: WARN
  pattern:
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{50} - %msg%n"

12. 最佳实践与建议

12.1 异常处理原则

  1. 统一格式:所有异常响应使用统一格式
  2. 友好信息:向用户返回友好的错误信息
  3. 安全考虑:不暴露系统内部信息
  4. 监控告警:建立异常监控和告警机制

12.2 性能优化建议

  1. 异步处理:异常处理采用异步方式
  2. 缓存策略:缓存常见异常响应
  3. 批量处理:批量处理异常日志

13. 总结

Spring Cloud Gateway的异常处理是构建健壮微服务系统的重要组成部分。通过合理的异常处理机制,可以提供友好的错误响应,保障系统稳定性,并建立完善的监控体系。在实际应用中,需要根据业务需求设计合适的异常处理策略。

14. 参考资料

  1. Spring WebFlux异常处理文档
  2. Spring Cloud Gateway异常处理指南
  3. 微服务异常处理最佳实践
  4. API错误响应设计规范

作者信息:本文详细介绍了Spring Cloud Gateway异常处理与错误响应,适合有一定微服务开发经验的开发者阅读。

注意事项:在生产环境中,应避免返回详细的系统错误信息,以防安全风险。

扩展阅读:如需深入了解响应式编程异常处理,请参考相关技术文档。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CarlowZJ

我的文章对你有用的话,可以支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值