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

Scala ZIO:如何从ZIO返回自定义错误适配tapir请求

Tapir+Sttp Client3+ZIO自定义错误处理实现

问题场景

使用Tapir和sttp.client3.json._发起请求,原本代码返回Task[MyResponse],现在需要修改为返回IO[MyError, MyResponse],实现:

  • 根据HTTP状态码返回对应自定义错误
  • JSON解析失败时返回自定义错误

已定义错误层级:

sealed trait MyError extends Throwable
case class MyFirstError(msg: String) extends MyError 
case class MySecondError(msg: String) extends MyError

原尝试代码因错误类型不匹配报错:Required IO[MyError, MyResponse], Found ZIO[Any, Throwable, MyResponse]


解决方案

1. 完善错误类型(可选但推荐)

新增JSON解析错误的自定义类型,让错误语义更清晰:

sealed trait MyError extends Throwable
case class MyFirstError(msg: String) extends MyError 
case class MySecondError(msg: String) extends MyError
case class MyParseError(msg: String) extends MyError // 用于处理JSON解析失败

2. 修改请求处理代码

def method(...): IO[MyError, MyResponse] = 
  someLogic().response(asJson[MyResponse]).flatMap { res =>
    res.code match {
      case StatusCode.BadRequest => 
        ZIO.fail(MyFirstError("请求参数错误"))
      case StatusCode.Forbidden => 
        ZIO.fail(MySecondError("无访问权限"))
      case code if code.isSuccess => 
        // 将JSON解析的Throwable错误转换为自定义MyError
        ZIO.fromEither(res.body.left.map(parseErr => MyParseError(s"JSON解析失败: ${parseErr.getMessage}")))
      case otherCode => 
        // 处理其他未覆盖的错误状态码
        ZIO.fail(MyFirstError(s"请求失败,状态码: $otherCode"))
    }
  }

关键修改说明

  • 统一错误类型:将res.body的Either[Throwable, MyResponse]通过left.map转换为Either[MyError, MyResponse],确保ZIO的错误类型为MyError,匹配方法返回类型要求。
  • 分支全覆盖:明确区分成功状态码与各类错误状态码,避免出现未处理的分支导致类型不兼容。
  • 移除orDie:orDie会直接抛出Throwable,破坏错误类型的统一性,改用错误转换实现自定义错误抛出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:01:35