Универсальный Спрей-Клиент
Я пытаюсь создать универсальный HTTP-клиент в Scala, используя спрей. Вот определение класса:
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.
object AllJsonFormats
extends DefaultJsonProtocol with SprayJsonSupport with MetaMarshallers with MetaToResponseMarshallers with NullOptions {
Конечно, у этого объекта есть определения для сериализации models.api.Space, models.api.Failure и models.api.Success.
Space
Тип выглядит нормально, т.е. когда я говорю универсальному методу, что он будет получать и возвращать Space
ошибок нет. Но как только я ввожу Either в вызов метода, я получаю следующую ошибку компилятора:
не удалось найти неявное значение для параметра улик типа spray.httpx.unmarshalling.Unmarshaller[Either[models.api.Failure,models.api.Success]].
Я ожидал, что любой из них, подразумеваемый в spray.json.DefaultJsonProtocol, то есть в spray.json.StandardFormts, покроет меня.
Ниже приведен мой класс HttpClient, который пытается быть универсальным: Обновление: образец кода с более ясным или повторяющимся кодом
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")
}
С вышеизложенным, проблема все еще возникает, когда unmarshaller не решен. Помощь будет принята с благодарностью..
Благодарю.
1 ответ
Spray определяет маршаллер по умолчанию для Either[A,B]
если Marshaller[A]
а также Marshaller[B]
находятся в определенной области внутри MetaMarshallers
черта характера. Но движение в другом направлении требует Unmarshaller
, Вам нужно будет определить область действия Unmarshaller
за Either[Failure, Success]
, Это не может быть закодировано без конкретных знаний об ожидаемом ответе и о том, какой будет стратегия для выбора: отменить ответ как Left
или как Right
, Например, предположим, что вы хотите вернуть Failure для ответа, отличного от 200, и Success из тела ответа 200 json:
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(...)))
}
}
Обновить
Если заглянуть глубже, проблема заключается в том, что по умолчанию eitherFormat
в spray.json.StandardFormats
это не RootJsonFormat
, который требуется по умолчанию в JSON unmarshaller, определенный в spray.httpx.SprayJsonSupport
, Определение следующего неявного метода должно решить проблему:
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