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
слой.