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
的函数,因此它会在那个时间点查找 T
的 implicit
。由于在范围内找不到,因此无法编译。
为了解决这个问题,只需将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
被调用时,当 A
为 Int
时,用户不需要导入或定义任何 implicit
vals
或String
因为如果 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
的函数,因此它会在那个时间点查找 T
的 implicit
。由于在范围内找不到,因此无法编译。
为了解决这个问题,只需将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
被调用时,当 A
为 Int
时,用户不需要导入或定义任何 implicit
vals
或String
因为如果 scala 在当前范围内找不到任何隐式定义,它会自动在伴随对象中查找任何隐式定义。