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

Scala、ZIO、Sttp、Circe:如何将错误响应映射到自定义Trait

问题解决思路

核心原因

Circe对密封特质(sealed trait)的自动解码依赖鉴别器字段来区分不同子类。你的服务器返回的错误JSON中没有携带标识子类类型的字段,导致Circe无法判断应该解码为GeneralTrait的哪个实现类,从而抛出DeserializationException。而直接使用具体类时,Circe无需鉴别器,直接匹配字段即可完成解码,所以能正常工作。

两种解决方案

方案1:添加鉴别器字段并配置Circe

如果可以修改服务器返回的错误响应结构,在JSON中加入鉴别器字段(比如"type"),同时配置Circe使用该字段派生编解码器:

  1. 服务器返回的错误JSON示例:
{
  "type": "FirstError",
  "msg": "请求参数错误",
  "code": 400
}
  1. 修改Circe编解码器配置:
import io.circe._
import io.circe.generic.extras._
import io.circe.generic.extras.semiauto._

// 配置鉴别器字段名称
implicit val circeConfig: Configuration = 
  Configuration.default.withDiscriminator("type")

// 先派生子类编解码器
implicit val firstErrorCodec: Codec[FirstError] = deriveConfiguredCodec
// 再派生密封特质编解码器
implicit val generalTraitCodec: Codec[GeneralTrait] = deriveConfiguredCodec

这样Circe就能通过type字段识别对应的子类,完成GeneralTrait的自动解码。

方案2:手动根据状态码解码

如果无法修改服务器响应,可先将错误体解析为原始Json,再结合HTTP状态码手动匹配解码:

basicRequest.auth
  .get(...)
  .response(asJsonEither[Json, MyResponse]) // 错误体先解析为原始Json
  .send(backend)
  .map(_.body.left.map {
    case HttpError(jsonBody, statusCode) => 
      statusCode match {
        // 根据状态码匹配对应的子类解码
        case StatusCode.BadRequest => 
          jsonBody.as[FirstError].fold(
            _ => SomeSerializationError("FirstError格式错误"),
            identity
          )
        // 添加其他状态码对应的子类处理逻辑
        case StatusCode.NotFound => 
          jsonBody.as[SecondError].fold(...)
        case _ => SomeSerializationError("未知错误类型")
      }
    case DeserializationException(_, _) => SomeSerializationError("反序列化失败")
  })
  .flatMap(ZIO.fromEither(_))

这种方式跳过了Circe对密封特质的自动鉴别,直接通过业务逻辑(状态码)绑定子类,更灵活适配无鉴别器的响应结构。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 05:03:37