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
相关产品推荐
相关产品推荐

