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

借助ZIO、zio-http与Sttp Client实现多环境兼容的Coingecko客户端API咨询

核心问题根源

你当前的设计瓶颈在于CoingeckoApiClient接口绑定死了同步返回Either类型,导致异步实现必须硬转同步,这也是你需要调用unsafeRun的核心原因。参考sttp的多后端设计思路,只要把接口泛型化,就能无痛支持多技术栈适配,且不需要重写核心逻辑。


具体改造方案

1. 重构基础接口为泛型效果类型

将接口返回值绑定到泛型参数F[_],适配不同技术栈的效果类型(同步用Identity、ZIO用Task/IO、Future用Future):

trait CoingeckoApiClient[F[_]] extends CoingeckoClient {
  // ping方法自动适配对应效果类型
  override def ping: F[Either[CoingeckoApiError, PingResponse]] =
    get[PingResponse](endpoint = "ping", QueryParams())

  def get[T](endpoint: String, queryParams: QueryParams)(
    using Format[T]
  ): F[Either[CoingeckoApiError, T]]
}

你原有CoingeckoApi[F[_], P]的签名完全不需要修改,天生已经适配sttp的多后端特性。

2. 同步实现完全兼容原有逻辑

同步场景下F取Identity类型,原有逻辑一行不用改:

import cats.Id // 等同于scala.util.Identity
class CoingeckoApiBasic(api: CoingeckoApi[Id, Any]) extends CoingeckoApiClient[Id] {
  def get[T](endpoint: String, queryParams: QueryParams)(using Format[T]): Either[CoingeckoApiError, T] =
    api.get(endpoint, queryParams).body match {
      case Left(json) =>
        Json.parse(json).validate[CoingeckoApiError] match {
          case JsSuccess(value, _) => Left(value)
          case JsError(_) => Left(CoingeckoApiError.internalApiError(Some("Unknown Api Error")))
        }
      case Right(json) =>
        Json.parse(json).validate[T] match {
          case JsSuccess(value, _) => Right(value)
          case JsError(_) => Left(CoingeckoApiError.internalApiError(Some(s"Invalid Response for $endpoint")))
        }
    }
}

3. ZIO异步实现完全不需要Runtime

库层面绝对不要硬编码Runtime.unsafeRun,Runtime由上层应用侧负责初始化和运行,你只需要返回ZIO效果即可:

import zio.Task
class CoingeckoApiZIO(api: CoingeckoApi[Task, Any]) extends CoingeckoApiClient[Task] {
  def get[T](endpoint: String, queryParams: QueryParams)(using Format[T]): Task[Either[CoingeckoApiError, T]] =
    api.get(endpoint, queryParams).map { r =>
      // 完全复用同步场景的解析逻辑,不需要改动
      r.body match {
        case Left(json) =>
          Json.parse(json).validate[CoingeckoApiError] match {
            case JsSuccess(value, _) => Left(value)
            case JsError(_) => Left(CoingeckoApiError.internalApiError(Some("Unknown Api Error")))
          }
        case Right(json) =>
          Json.parse(json).validate[T] match {
            case JsSuccess(value, _) => Right(value)
            case JsError(_) => Left(CoingeckoApiError.internalApiError(Some(s"Invalid Response for $endpoint")))
          }
      }
    }
}

4. Future场景同理可快速扩展

import scala.concurrent.Future
import scala.concurrent.ExecutionContext

class CoingeckoApiFuture(api: CoingeckoApi[Future, Any])(using ExecutionContext) extends CoingeckoApiClient[Future] {
  def get[T](endpoint: String, queryParams: QueryParams)(using Format[T]): Future[Either[CoingeckoApiError, T]] =
    api.get(endpoint, queryParams).map { r =>
      // 同样复用完全相同的解析逻辑
      r.body match {
        // 解析代码和同步场景完全一致
      }
    }
}

补充说明

如果需要给同步场景用户保留原有直接返回Either的使用习惯,可以额外提供一个同步专用的语法扩展,不需要改动核心适配逻辑。所有技术栈的核心HTTP调用、响应解析逻辑100%复用,只需要对应实现不同效果类型的包装层即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 13:36:04