Scala 中的通用 REST 客户端

Generic REST client in Scala

我对 Scala 有点陌生,我正在尝试为我想使用的 RESTful api 编写一个通用客户端。我能够为特定情况提供具体的 Reads[T]Writes[T] 类 我想实例化我的客户端,但是编译器希望找到 Reads[T]Writes[T] 适用于任何类型,而不仅仅是我正在使用的类型。一些代码来说明(我省略了不相关的部分):

我的通用客户端:

class RestModule[T](resource: String, config: Config) ... with JsonSupport{
...
def create(item: T): Future[T] = {
    val appId = config.apiId
    val path = f"/$apiVersion%s/applications/$appId%s/$resource"

    Future {
      val itemJson = Json.toJson(item)
      itemJson.toString.getBytes
    } flatMap {
      post(path, _)
    } flatMap { response =>
      val status = response.status
      val contentType = response.entity.contentType

      status match {
        case Created => contentType match {
          case ContentTypes.`application/json` => {
            Unmarshal(response.entity).to[T]
          }
          case _ => Future.failed(new IOException(f"Wrong Content Type: $contentType"))
        }
        case _ => Future.failed(new IOException(f"HTTP Error: $status"))
      }
    }
...
}

JsonSupprt 特征:

trait JsonSupport {
    implicit val accountFormat = Json.format[Account]
}

我只实例化为 RestModule[Account]("accounts",config) 但我收到错误

Error:(36, 32) No Json serializer found for type T. Try to implement an implicit Writes or Format for this type.
  val itemJson = Json.toJson(item)
                           ^

当 T 只能是 Account 类型时,为什么编译器认为它需要类型 T 的 Writes?有什么办法可以解决这个问题吗?

编译器不喜欢您正在做的事情的原因与 implicit 参数的解析方式有关,更重要的是 何时 它们被解析。

考虑片段,

Object MyFunc {
   def test()(implicit s: String): String = s
}

implicit参数只在调用函数时通过参数解析,基本展开为,

MyFunc.test()(resolvedImplicit)

在您的特定情况下,您实际上调用了需要 implicit 的函数,因此它会在那个时间点查找 Timplicit。由于在范围内找不到,因此无法编译。

为了解决这个问题,只需将implicit参数添加到create方法中,告诉编译器在您调用create而不是[=22=时解决它] 在 create.

此外,我们可以使用 scala 的隐式规则来获得您想要的行为。

让我们来看看你的特质 Reads,

trait Reads[A] {
}

object MyFunc {
  def create[A](a: A)(implicit reads: Reads[A]): Unit = ???
}

正如我们之前所说,如果 implicit 在范围内,您可以调用它。但是,在这种您已经预定义读取的特殊情况下,我们实际上可以将它放在伴随对象中,

object Reads {
  implicit val readsInt: Reads[Int] = ???
  implicit val readsString: Reads[String] = ???
}

这样当 create 被调用时,当 AInt 时,用户不需要导入或定义任何 implicit valsString 因为如果 scala 在当前范围内找不到任何隐式定义,它会自动在伴随对象中查找任何隐式定义。