You need to enable JavaScript to run this app. Combine转换Publisher报“Receive canceled”,token过期重试请求不生效如何解决
优惠活动
大模型
产品
解决方案
定价
更多

Combine转换Publisher报“Receive canceled”,token过期重试请求不生效如何解决

Combine Token自动刷新流取消问题排查与修复

核心原因

你遇到的received canceled提示,本质是Combine流中出现了未被正确捕获的类型不匹配/错误穿透,导致整个订阅链路被主动取消,错误没有走到tryCatch处理逻辑中。

排查步骤

  • 先校验Publisher的错误类型是否对齐:session.publisher(for:mapper:token:)的Failure类型必须和tryCatch返回的Publisher Failure类型完全一致。如果session返回的是自定义错误类型,没有抹成通用Error,会因为类型不匹配被Combine隐式取消流,错误不会进入tryCatch。
  • 检查错误类型匹配逻辑:你当前判断error as? ServerErrorType,如果session抛出的错误被URLSession错误、解码错误等外层包装过,就会导致类型转换失败,直接抛出未处理的错误触发取消。可以在tryCatch开头先打印error的实际类型和内容,确认匹配逻辑是否生效。
  • 检查flatMap的类型兼容性:Combine默认的flatMap要求内部Publisher的Failure类型和上游authentificator.refreshToken的Failure类型完全一致,类型不匹配就会触发流取消。
  • 检查refreshToken自身的错误处理:如果强制刷新token的请求本身失败(比如refresh token也过期),这个错误没有被捕获的话也会直接穿透触发取消。

修复后的代码示例

public func executeRequest<T: Decodable, E: ServerErrorType>(
  _ request: HTTPRequest,
  mapper: ObjectMapper<T, E>
) -> AnyPublisher<(T, HTTPResponse), Error> {
      return authentificator
        .refreshToken(force: false)
        // 统一上游错误类型为通用Error
        .mapError { $0 as Error }
        .subscribe(on: DispatchQueue.global())
        // 改用tryFlatMap允许内部抛出错误
        .tryFlatMap { token -> AnyPublisher<(T, HTTPResponse), Error> in
          session.publisher(for: request, mapper: mapper, token: token)
            // 统一请求的错误类型为通用Error
            .mapError { $0 as Error }
            .tryCatch({ error -> AnyPublisher<(T, HTTPResponse), Error> in
              // 先打印错误调试
              debugPrint("收到请求错误: \(error) 类型: \(type(of: error))")
              if let error = error as? E, // 直接用泛型E匹配,避免类型转换失败
                 error.isAuthError {
                return authentificator
                  .refreshToken(force: true)
                  .mapError { $0 as Error }
                  .subscribe(on: DispatchQueue.global())
                  .flatMap { token -> AnyPublisher<(T, HTTPResponse), Error> in
                    session.publisher(for: request, mapper: mapper, token: token)
                      .mapError { $0 as Error }
                      .eraseToAnyPublisher()
                  }
                  // 可选:添加refresh token失败的兜底处理,避免穿透
                  .tryCatch { refreshError -> AnyPublisher<(T, HTTPResponse), Error> in
                    // 可以在这里触发登出逻辑
                    throw refreshError
                  }
                  .eraseToAnyPublisher()
              } else {
                throw error
              }
            })
            .print()
            .eraseToAnyPublisher()
        }
        .receive(on: DispatchQueue.main)
        .print()
        .eraseToAnyPublisher()
 }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 07:27:03