使用ZIO Env运行{4s服务器}

时间:2019-10-24 19:45:38

标签: scala http4s zio

尝试学习使用ZIO库,因此我决定创建一个基本的Web服务应用程序。想法很基本,将http4s lib用于服务器并路由端点,在端点调用上打印“ hello world”。

借助我发现的文档和示例,生成代码:

object Main extends ManagedApp {

  type AppEnvironment = Clock with Console with HelloRepository
  type AppTask[A] = RIO[AppEnvironment, A]

  override def run(args: List[String]): ZManaged[ZEnv, Nothing, Int] = {
    val httpApp: HttpApp[AppTask] = Router[AppTask]("/" -> helloWorldService).orNotFound

    val server = ZIO.runtime[AppEnvironment].flatMap { implicit rts =>
      BlazeServerBuilder[AppTask]
        .bindHttp(8080, "0.0.0.0")
        .withHttpApp(CORS(httpApp))
        .serve
        .compile[AppTask, AppTask, ExitCode]
        .drain
    }

    (for {
      _ <- ZManaged.environment[ZEnv] >>> server.toManaged_
    } yield ())
      .foldM(err => putStrLn(s"Execution failed with: $err").as(1).toManaged_, _ => ZManaged.succeed(0))
  }

  val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask]
  import dsl._

  val helloWorldService: HttpRoutes[AppTask] = HttpRoutes.of[AppTask] {
    case GET -> Root / "hello" / name => Ok(Repo.getHello(name))
  }
}

trait HelloRepository extends Serializable {
  val helloRepository: HelloRepository.Service[Any]
}

object HelloRepository extends Serializable {
  trait Service[R] extends Serializable {
    def getHello(name: String): ZIO[R, Nothing, String]
  }
}

object Repo extends HelloRepository.Service[HelloRepository] {
  override def getHello(name: String): ZIO[HelloRepository, Nothing, String] = ZIO.succeed(s"Hello $name")
}
  • 我创建路由器:Router[AppTask]("/" ...
  • 我创建服务器:ZIO.runtime[AppEnvironment].flatMap ...
  • 然后尝试使用ZIO环境启动服务器, 但我在此行中缺少以下内容: _ <- ZManaged.environment[ZEnv] >>> server.toManaged_ 不正确,并在构建时引发错误: 错误:(34,39)推断的类型参数[touch.Main.AppEnvironment,Throwable,Unit]不符合方法>>>的类型参数范围[R1>:zio.ZEnv,E1,B]   _ <-ZManaged.environment [ZEnv] >>> server.toManaged _

Error:(34, 39) inferred type arguments [touch.Main.AppEnvironment,Throwable,Unit] do not conform to method >>>'s type parameter bounds [R1 >: zio.ZEnv,E1,B]

Error:(34, 50) type mismatch; found : zio.ZManaged[touch.Main.AppEnvironment,Throwable,Unit] (which expands to) zio.ZManaged[zio.clock.Clock with zio.console.Console with touch.HelloRepository,Throwable,Unit] required: zio.ZManaged[R1,E1,B]

也许有人可以帮助我提供正确的语法? 也会解释一些解释,或链接到解释该问题的文档。

1 个答案:

答案 0 :(得分:3)

我想解释更多,但是我不知道您的代码示例在哪里或您的build.sbt是什么样子,但是我碰巧有一些http4s代码,所以我随意添加了一些{ {1}}语句并将其简化。您总是可以增加我所花的复杂性。

这对我有用。

/tmp/http4s/test.scala

import

/tmp/http4s/build.sbt

import org.http4s.implicits._
import org.http4s.server.blaze._
import org.http4s.server.Router
import org.http4s.server.middleware.CORS

import org.http4s._
import org.http4s.dsl.Http4sDsl

import zio._
import zio.clock._
import zio.console._
import zio.interop.catz._

trait HelloRepository
{
  def getHello(name: String): ZIO[AppEnvironment, Nothing, String]
}

trait AppEnvironment extends Console with Clock
{
  val helloRepository: HelloRepository
}

object Main extends App {

  type AppTask[A] = RIO[AppEnvironment, A]

  val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask]
  import dsl._

  val httpApp: HttpApp[AppTask] = Router[AppTask](
    "/" -> HttpRoutes.of[AppTask] {
      case GET -> Root / "hello" / name => Ok( ZIO.accessM[AppEnvironment](_.helloRepository.getHello(name)) )
    }
  ).orNotFound

  val program = for {
    server <- ZIO.runtime[AppEnvironment]
    .flatMap {
      implicit rts =>
        BlazeServerBuilder[AppTask]
          .bindHttp(8080, "0.0.0.0")
          .withHttpApp(CORS(httpApp))
          .serve
          .compile
          .drain
    }
  } yield server

  val runEnv = new AppEnvironment with Console.Live with Clock.Live
  {
    val helloRepository = new HelloRepository
    {
      def getHello(name: String): ZIO[AppEnvironment, Nothing, String] = ZIO.succeed(s"Hello $name")
    }
  }

  def run(args: List[String]) =
    program
      .provide(runEnv)
      .foldM(err => putStrLn(s"Execution failed with: $err") *> ZIO.succeed(1), _ => ZIO.succeed(0))
}

示例执行

val Http4sVersion       = "0.20.0"
val CatsVersion         = "2.0.0"
val ZioCatsVersion      = "2.0.0.0-RC3"
val ZioVersion          = "1.0.0-RC13"
val LogbackVersion      = "1.2.3"

lazy val root = (project in file("."))
  .settings(
    organization := "example",
    name := "example",
    version := "0.0.1-SNAPSHOT",
    scalaVersion := "2.12.8",
    scalacOptions ++= Seq("-Ypartial-unification"),
    libraryDependencies ++= Seq(
      "org.typelevel"              %% "cats-effect"         % CatsVersion,
      "dev.zio"                    %% "zio"                 % ZioVersion,
      "dev.zio"                    %% "zio-interop-cats"    % ZioCatsVersion,
      "org.http4s"                 %% "http4s-blaze-server" % Http4sVersion,
      "org.http4s"                 %% "http4s-dsl"          % Http4sVersion,
      "ch.qos.logback"             %  "logback-classic"     % LogbackVersion,
   ),
    addCompilerPlugin("org.spire-math" %% "kind-projector"     % "0.9.6"),
    addCompilerPlugin("com.olegpy"     %% "better-monadic-for" % "0.2.4")
  )

scalacOptions ++= Seq(
  "-deprecation",               // Emit warning and location for usages of deprecated APIs.
  "-encoding", "UTF-8",         // Specify character encoding used by source files.
  "-language:higherKinds",      // Allow higher-kinded types
  "-language:postfixOps",       // Allows operator syntax in postfix position (deprecated since Scala 2.10)
  "-feature",                   // Emit warning and location for usages of features that should be imported explicitly.
  "-Ypartial-unification",      // Enable partial unification in type constructor inference
  "-Xfatal-warnings",           // Fail the compilation if there are any warnings
)

打开http://localhost:8080/hello/there之后,我在浏览器中观察到了预期的输出。

希望这会有所帮助。