Универсальный Спрей-Клиент

Я пытаюсь создать универсальный 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

Другие вопросы по тегам