Sttp Client 3 с несоответствием типа ZLayer

Я использую ZLayer и Sttp Client (асинхронный для создания простого приложения HTTP-запроса, но я обнаружил ошибку несоответствия типа, которую не смог решить. Может ли кто-нибудь сказать мне, почему я получаю ошибку несоответствия типа?

Я использую эти версии scala и библиотек.

java -> 8.282.08.1-amzn

scala -> с.13.5

dev.zio -> 1.0.7

com.softwaremill.sttp.client3 -> 3.3.0

      type mismatch;
 found   : zio.ZLayer[sttp.client3.asynchttpclient.zio.SttpClient,Nothing,ZlayerAndSttp.HttpBin]
    (which expands to)  zio.ZLayer[zio.Has[sttp.client3.SttpBackend[zio.Task,sttp.capabilities.zio.ZioStreams with sttp.capabilities.WebSockets]],Nothing,zio.Has[ZlayerAndSttp.HttpBin.Service]]
 required: zio.ZLayer[ZlayerAndSttp.HttpBin,?,?]
    (which expands to)  zio.ZLayer[zio.Has[ZlayerAndSttp.HttpBin.Service],?,?]
    program.provideCustomLayer((AsyncHttpClientZioBackend.layer() >>> HttpBin.live) >>> HttpBin.live)

Вот весь код

      import zio._
import sttp.client3._
import sttp.client3.circe._
import sttp.client3.asynchttpclient.zio._
import io.circe.generic.auto._
import zio.console.Console


object ZlayerAndSttp extends App {

  case class HttpBinResponse(origin: String, headers: Map[String, String])

  type HttpBin = Has[HttpBin.Service]
  object HttpBin {
    trait Service {
      def sendRequest: ZIO[HttpBin with SttpClient, Throwable, HttpBinResponse]
    }

    val live: ZLayer[SttpClient, Nothing, HttpBin] = ZLayer.succeed(new Service {
      override def sendRequest: ZIO[HttpBin with SttpClient, Throwable, HttpBinResponse] = {
        val request = basicRequest
          .get(uri"https://httpbin.org/get")
          .response(asJson[HttpBinResponse])
        sendR(request).map(_.body).absolve.map(res => HttpBinResponse(res.origin, res.headers))
      }
    })

    def sendRequest: ZIO[HttpBin with SttpClient, Throwable, HttpBinResponse] = ZIO.accessM(_.get.sendRequest)
  }

  val request = basicRequest
    .get(uri"https://httpbin.org/get")
    .response(asJson[HttpBinResponse])

  override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
    val program = for {
      result <- HttpBin.sendRequest
      _ <- console.putStrLn(s"${result.origin}, ${result.headers}")
    } yield ()
    program.provideCustomLayer((AsyncHttpClientZioBackend.layer() >>> HttpBin.live) >>> HttpBin.live) // type mismatch
      .exitCode

    // ↓these lines of code run with no errors but I can't understand why
//    val program: ZIO[Console with SttpClient, Throwable, Unit] = for {
//      response <- send(request)
//      _ <- console.putStrLn(s"${response.body.toString}")
//    } yield ()
//    program.provideCustomLayer(AsyncHttpClientZioBackend.layer()).exitCode
  }

}

1 ответ

Кажется, ты делаешь свою жизнь немного сложнее, чем нужно.

Как будет выглядеть конечный код, зависит от того, чего вы хотите здесь достичь. Если вы пытаетесь скрыть использование Sttp за интерфейсом, тогда ваше определение слоя должно выглядеть так:

          val live: ZLayer[SttpClient, Nothing, HttpBin] = 
      (for {
        client <- ZIO.environment[SttpClient]
      } yield new Service {
          override def sendRequest: ZIO[Any, Throwable, HttpBinResponse] = {
            val request = basicRequest
              .get(uri"https://httpbin.org/get")
              .response(asJson[HttpBinResponse])

            sendR(request)
              .map(_.body)
              .absolve
              .map(res => HttpBinResponse(res.origin, res.headers))
              .provide(client)
  }
      }).toLayer

Тогда ваш метод доступа станет:

      def sendRequest: ZIO[HttpBin, Throwable, HttpBinResponse] = 
  ZIO.accessM(_.get.sendRequest)

И вы можете использовать его с:

        override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
    val program = for {
      result <- HttpBin.sendRequest
      _ <- console.putStrLn(s"${result.origin}, ${result.headers}")
    } yield ()

    program
      .provideCustomLayer(AsyncHttpClientZioBackend.layer() >>> HttpBin.live)
      .exitCode

Здесь следует отметить, что для композиции слоев я использую только вертикальный оператор, потому что HttpBin.live зависит от слоя, но мы «скрываем» этот факт от вызывающего метода, чтобы вы могли создать test вариант HttpBin для этого не требовалось Sttp в случае необходимости.

Если вам не нужно скрывать информацию, вы можете вместо этого полностью удалить промежуточный слой и просто обработать свой sendRequest как самостоятельный метод.

      object HttpBin {
  def sendRequest: ZIO[SttpClient, Throwable, HttpBinResponse] = {
     val request = basicRequest
       .get(uri"https://httpbin.org/get")
       .response(asJson[HttpBinResponse])

     sendR(request)
       .map(_.body)
       .absolve
       .map(res => HttpBinResponse(res.origin, res.headers))
}

Затем вы можете просто вызвать этот метод, и все, что вам нужно сделать, это предоставить SttpClient слой.

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