开源项目
skydoves/sandwich avatar
skydoves/sandwich

Sandwich 2.4.0:用密封类型统一 Retrofit 与 Ktor 的响应处理

项目速览:Sandwich 是一个适应性强的轻量级密封 API 库,旨在处理 Kotlin for Retrofit、Ktor 和 Kotlin Multiplatform 中的 API 响应和异常。

1,773 个 Star114 个 ForkKotlinApache-2.0

秒懂

它是什么?
Sandwich 是一个面向 Kotlin 的轻量级密封 API 库,为 Retrofit、Ktor 和 Kotlin Multiplatform 提供统一的响应模型。它用 Success、Failure.Error 和 Failure.Exception 三种类型替代手写 Resource 包装类,但代价是引入一层抽象,需要在简洁性和调试直观性之间做取舍。
适合谁用?
Sandwich 适合那些已经在使用 Retrofit 或 Ktor、并且愿意接受统一响应抽象的开发团队。如果你的项目大量依赖后端返回的错误码分支,或者需要跨平台共享网络层,它能把重复的 when 分支收拢成三个密封子类型。
能商用吗?
可以。Apache-2.0 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
还在维护吗?
在维护。仓库最近一次提交在 2 天前。
用什么语言写的?
主要是 Kotlin(依据 GitHub 的语言统计)。

以上回答依据项目的 GitHub 数据(最近同步于 2026年9月18日)和我们的分析,不构成法律意见。

开源项目深度解析

它在解决哪个具体问题

网络请求的返回无非三种情况:成功拿到数据、服务器返回了错误状态码、客户端在发请求或解析响应时抛了异常。大多数 Android 项目会为这三种情况各写一个包装类,名字通常叫 Resource 或 Result,然后每个仓库层都要手动把 Retrofit 的 Response 转换成这个包装类。Sandwich 把这一步收进了库本身。它定义了一个叫 ApiResponse 的密封接口,下分 Success、Failure.Error 和 Failure.Exception 三个子类型。接口声明在核心模块 sandwich 里,Retrofit 和 Ktor 的适配模块负责把各自的响应对象自动映射成这三种类型。面向的受众很明确:用 Kotlin 写 Android 或后端、并且已经选了 Retrofit 或 Ktor 作为 HTTP 客户端的团队。库本身不发起网络请求,它只做一层结果转换和操作符封装。

ApiResponse 的三个子类型怎么用

Success 持有 data 字段,还有一个可选的 tag 属性,用来标注数据来源或做后续处理。Failure.Error 对应 4xx 和 5xx 这类服务器明确拒绝的请求,它带一个 payload 字段,可以塞进错误响应体。Failure.Exception 则捕获客户端侧的异常,比如连接超时,它暴露 exception 和 message。关键设计是这两个失败类型都可以被继承。文档里给了例子:可以定义 data object LimitedRequest 继承 Failure.Error,payload 设为 "your request is limited",或者定义 HttpException 继承 Failure.Exception。这样业务层可以用 when 表达式直接匹配具体的错误子类型,编译器会强制你处理所有分支。这个机制比用整数错误码或字符串判断要严格得多,代价是你必须为每一种错误情况写一个类。

从 Gradle 依赖到第一个响应对象

引入方式分两条路。Android 项目在模块的 build.gradle 里加三行依赖:先声明 sandwich-bom 2.4.0 作为平台版本控制,再引 sandwich 核心库和 sandwich-retrofit 适配层,测试时加 sandwich-test。Kotlin Multiplatform 项目则要改 build.gradle.kts 的 commonMain,除了核心库,还要加 sandwich-ktor、sandwich-ktor-serialization 和 sandwich-ktorfit 三个模块。README 里没有贴出具体的初始化代码,只给了依赖坐标,完整的接入步骤在项目文档站里。有一点值得注意:依赖坐标的 group 是 com.github.skydoves,不是常见的 com.squareup 或 io.ktor,这意味着你需要接受第三方维护者的发布节奏。R8 规则已经打包进了 JAR,Android 项目开启混淆时不需要额外写 keep 规则。

Mapper 和 Operator:数据转换的两层工具

库提供了 Mapper 和 Operator 两套工具,但 README 对它们的描述很简略,只说它们是特色功能,没有给出具体 API 示例。从命名和文档站的结构可以推断,Mapper 负责把 ApiResponse 里的数据映射成领域模型,Operator 则用于在响应链上做全局处理,比如统一打印日志或注入公共请求头。这种设计思路是把响应处理拆成两个阶段:先映射数据,再操作结果。实际使用时,你需要自己去文档站翻每个函数的签名。这种信息密度偏低的情况在开源库里不算罕见,但它意味着上手成本比 README 展示的示例代码要高。一个直接后果是:如果你只想用 Success 和 Failure 两个分支,Sandwich 的抽象层级可能反而比手写 when 更绕。

一个真实的限制:抽象掩盖了底层细节

Sandwich 把 Retrofit 的 Response 和 Ktor 的 HttpResponse 统一成 ApiResponse,这带来了分支穷尽的好处,也抹掉了底层库的原始信息。比如 Retrofit 的 Response 里有 raw() 方法可以拿到原始 okhttp3.Response,里面有 HTTP 头、缓存策略、重定向历史。一旦转换成 ApiResponse.Success,这些细节就只剩 data 和 tag。如果你的调试流程依赖查看原始响应头,或者需要区分 301 和 302 这类重定向状态码,Sandwich 的模型里没有对应的字段。另一个隐患是 Failure.Error 的 payload 类型是泛型,你需要自己决定它是什么类型,库不做反序列化。这意味着错误体的解析逻辑要由调用方实现,Sandwich 只负责传递。对于错误结构经常变化的 API,这个位置容易变成维护死角。

对比手写 Result 包装类:收益与代价

不引入 Sandwich 的常规做法是自己在项目里定义一个密封类,比如 SealedResult,然后写一个扩展函数把 Retrofit 的 Response 转换过去。这两种方案的差异不在最终的分支结构,而在转换逻辑放在哪里。手写方案里,转换函数是你自己的代码,你可以针对每个接口写不同的错误解析逻辑,也可以完全跳过转换直接返回 Response。Sandwich 把转换收进库的 CallAdapter 里,换来的是统一行为,失去的是每个接口的定制自由度。另一个实际区别是测试:sandwich-test 模块提供了针对 ApiResponse 的测试工具,手写方案则需要自己 mock Response 对象。对于已经有成熟网络层封装的中大型项目,迁移到 Sandwich 意味着要重写所有现有的响应转换代码,这个成本在 README 里没有提及。

维护状态与升级路径

仓库最近一次推送是 2026 年 7 月,2.4.0 版本在同一天发布,往前有 2.3.0 和 2.2.2,发布间隔大约一个月到两个月。这个节奏说明项目仍在活跃维护,不是被遗弃的状态。许可证是 Apache-2.0,对商用和修改都没有额外限制,也不需要你在分发时公开源码。升级方面,由于引入了 sandwich-bom 平台模块,版本对齐由 BOM 统一管理,你只需要改 BOM 的版本号即可。但要注意:BOM 只锁定 Sandwich 自身模块的版本,不管理 Retrofit 或 Ktor 的版本,这两者的兼容性需要你自己确认。仓库没有提供变更日志的链接,升级到新版本前,最好去 GitHub 的 Releases 页面看具体改动,特别是 2.x 版本之间是否有破坏性 API 变更。

编辑结论

Sandwich 适合那些已经在使用 Retrofit 或 Ktor、并且愿意接受统一响应抽象的开发团队。如果你的项目大量依赖后端返回的错误码分支,或者需要跨平台共享网络层,它能把重复的 when 分支收拢成三个密封子类型。不适合的场景是:你只需要一个简单的 Result 包装,或者你的团队更习惯在调用点直接看到 Retrofit 的原始异常类型。采用前需要验证三件事:一是确认 sandwich-retrofit 的 CallAdapter 与你的 Retrofit 版本兼容,二是检查 Ktor 插件的序列化配置是否匹配你的 ContentNegotiation 设置,三是用 sandwich-test 写一个针对错误 payload 的单元测试,确认自定义 Error 子类型能按预期触发。Sandwich 的价值建立在密封类的穷尽性上,一旦你开始用字符串判断错误类型,它就退化成普通包装类。

官方来源

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
社区笔记

社区笔记