通用喷雾客户 [英] Generic Spray-Client
问题描述
我正在尝试使用Spray在Scala中创建通用HTTP客户端.这是类的定义:
I'm trying to create a generic HTTP client in Scala using spray. Here is the class definition:
object HttpClient extends HttpClient
class HttpClient {
implicit val system = ActorSystem("api-spray-client")
import system.dispatcher
val log = Logging(system, getClass)
def httpSaveGeneric[T1:Marshaller,T2:Unmarshaller](uri: String, model: T1, username: String, password: String): Future[T2] = {
val pipeline: HttpRequest => Future[T2] = logRequest(log) ~> sendReceive ~> logResponse(log) ~> unmarshal[T2]
pipeline(Post(uri, model))
}
val genericResult = httpSaveGeneric[Space,Either[Failure,Success]](
"http://", Space("123", IdName("456", "parent"), "my name", "short_name", Updated("", 0)), "user", "password")
}
对象utils.AllJsonFormats
具有以下声明.它包含所有模型格式.在另一端"使用了相同的类,即,我还编写了API,并在其中使用了带有Spray-can和spray-json的相同格式化程序.
An object utils.AllJsonFormats
has the following declaration. It contains all the model formats. The same class is used on the "other end" i.e. I also wrote the API and used the same formatters there with spray-can and spray-json.
object AllJsonFormats
extends DefaultJsonProtocol with SprayJsonSupport with MetaMarshallers with MetaToResponseMarshallers with NullOptions {
当然,该对象具有对models.api.Space,models.api.Failure和models.api.Success的序列化的定义.
Of course that object has definitions for the serialization of the models.api.Space, models.api.Failure and models.api.Success.
Space
类型看起来不错,即当我告诉通用方法它将接收并返回Space
时,没有错误.但是,一旦将Either引入方法调用中,就会出现以下编译器错误:
The Space
type seems fine, i.e. when I tell the generic method that it will be receiving and returning a Space
, no errors. But once I bring an Either into the method call, I get the following compiler error:
找不到类型为证据的隐式值 spray.httpx.unmarshalling.Unmarshaller [Either [models.api.Failure,models.api.Success]].
could not find implicit value for evidence parameter of type spray.httpx.unmarshalling.Unmarshaller[Either[models.api.Failure,models.api.Success]].
我的期望是,spray.json.DefaultJsonProtocol中隐式的一个,即Spray.json.StandardFormts中的隐式,将覆盖我.
My expectation was that the either implicit in spray.json.DefaultJsonProtocol, i.e. in spray.json.StandardFormts, would have me covered.
以下是我的HttpClient类,尝试最好是通用的: 更新:更清晰/可重复的代码示例
The following is my HttpClient class trying it's best to be generic: Update: Clearer/Repeatable Code Sample
object TestHttpFormats
extends DefaultJsonProtocol {
// space formats
implicit val idNameFormat = jsonFormat2(IdName)
implicit val updatedByFormat = jsonFormat2(Updated)
implicit val spaceFormat = jsonFormat17(Space)
// either formats
implicit val successFormat = jsonFormat1(Success)
implicit val failureFormat = jsonFormat2(Failure)
}
object TestHttpClient
extends SprayJsonSupport {
import TestHttpFormats._
import DefaultJsonProtocol.{eitherFormat => _, _ }
val genericResult = HttpClient.httpSaveGeneric[Space,Either[Failure,Success]](
"https://api.com/space", Space("123", IdName("456", "parent"), "my name", "short_name", Updated("", 0)), "user", "password")
}
使用上述方法,仍然无法解决解组器的问题.帮助将不胜感激..
With the above, the problem still occurs where the unmarshaller is unresolved. Help would be greatly appreciated..
谢谢.
推荐答案
如果Marshaller[A]
和Marshaller[B]
在MetaMarshallers
特征内定义的范围内,则Spray为Either[A,B]
定义默认的编组器.但是,要执行其他方向则需要Unmarshaller
.您将需要为Either[Failure, Success]
定义范围内的Unmarshaller
.如果没有特定的预期响应以及如何选择是否将响应编组为Left
或Right
的策略,则无法对此进行编码.例如,假设您要在非200响应上返回失败",并从200 json响应正文中返回成功":
Spray defines a default marshaller for Either[A,B]
if a Marshaller[A]
and Marshaller[B]
are in defined scope inside the MetaMarshallers
trait. But, going the other direction requires an Unmarshaller
. You will need to define an in-scope Unmarshaller
for Either[Failure, Success]
. This cannot be coded without specific knowledge of the expected response and what the strategy will be for choosing whether to unmarshall a response as a Left
or as a Right
. For example, let's say you want to return a Failure on a non-200 response and a Success from a 200 json response body:
type ResultT = Either[Failure,Success]
implicit val resultUnmarshaller: FromResponseUnmarshaller[ResultT] =
new FromResponseUnmarshaller[ResultT] {
def apply(response: HttpResponse): Deserialized[ResultT] = response.status match {
case StatusCodes.Success(200) =>
Unmarshaller.unmarshal[Success](response.entity).right.map(Right(_))
case _ => Right(Left(Failure(...)))
}
}
更新
更深入地看,问题似乎在于spray.json.StandardFormats
中的默认eitherFormat
不是RootJsonFormat
,这是spray.httpx.SprayJsonSupport
中定义的默认JSON解组器所必需的.定义以下隐式方法应该可以解决该问题:
Looking deeper into this, the problem appears to be that the default eitherFormat
in spray.json.StandardFormats
is not a RootJsonFormat
, which is required by the default JSON unmarshaller defined in spray.httpx.SprayJsonSupport
. Defining the following implicit method should solve the issue:
implicit def rootEitherFormat[A : RootJsonFormat, B : RootJsonFormat] = new RootJsonFormat[Either[A, B]] {
val format = DefaultJsonProtocol.eitherFormat[A, B]
def write(either: Either[A, B]) = format.write(either)
def read(value: JsValue) = format.read(value)
}
我有一个可行的示例要点,希望可以解释您将如何使用它. https://gist.github.com/mikemckibben/fad4328de85a79a06bf3
I have an working example gist that hopefully explains how you would use this. https://gist.github.com/mikemckibben/fad4328de85a79a06bf3
这篇关于通用喷雾客户的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!