问题描述
我使用 ZLayer 和 Sttp Client(异步创建简单的 http 请求者应用程序,但我发现无法解决的类型不匹配错误。 谁能告诉我为什么会出现类型不匹配错误?
我使用这些版本的 Scala 和库。
java -> 8.282.08.1-amzn
scala -> s.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]],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,HttpBin] = ZLayer.succeed(new Service {
override def sendRequest: ZIO[HttpBin with SttpClient,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,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,Unit] = for {
// response <- send(request)
// _ <- console.putStrLn(s"${response.body.toString}")
// } yield ()
// program.provideCustomLayer(AsyncHttpClientZioBackend.layer()).exitCode
}
}
解决方法
你似乎让你的生活变得比需要的更复杂。
最终代码的样子取决于你想在这里实现什么。如果您试图在 Sttp
接口后面隐藏 HttpBin
的使用,那么您的图层定义应该如下所示:
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,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
依赖于带有 SttpClient
的层,但我们在调用方法中“隐藏”了这一事实,因此您如果需要,可以创建不需要 Sttp 的 test
的 HttpBin
变体。
如果您不需要隐藏信息,您可以完全移除中间层,并将您的 sendRequest
视为一个独立的方法。
object HttpBin {
def sendRequest: ZIO[SttpClient,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
层。