具体
http://spray.io/documentation/1.2.3/spray-client/
1.构建service
添加依赖
name:="demo10"
scalaVersion := "2.11.6"
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")
libraryDependencies ++= {
val akkaV = "2.3.9"
val sprayV = "1.3.3"
Seq(
"io.spray" %% "spray-can" % sprayV,
"io.spray" %% "spray-routing" % sprayV,
"io.spray" %% "spray-client" % sprayV,
"io.spray" %% "spray-util" % sprayV,
"io.spray" %% "spray-httpx" % sprayV,
"io.spray" % "spray-json_2.10" % "1.3.2",
"io.spray" %% "spray-http" % sprayV,
"io.spray" %% "spray-testkit" % sprayV % "test",
"com.typesafe.akka" %% "akka-actor" % akkaV,
"com.typesafe.akka" %% "akka-testkit" % akkaV % "test",
"org.specs2" %% "specs2-core" % "2.3.11" % "test"
)
}
编写service的关键就是Directive,我们说过Directive的作用,下面我们侧重操作Directive的组合
我们通过 | , & 来简单的组合
val rightPath = post & authenticate(BasicAuth(authenticator _, realm = "secure site")) & parameters('id.as[Int]).as(Order)
我们要求请求必须是post 且 带id参数的认证请求,
所以整个routing的配置如下
def authenticator(userPass: Option[UserPass]): Future[Option[String]] = Future {
//检查用户
if (userPass.exists(up => up.user == "admin" && up.pass == "admin")) Some(userPass.get.user)
else None
}
val rightPath = post & authenticate(BasicAuth(authenticator _, realm = "secure site")) & parameters('id.as[Int]).as(Order)
val route = sealRoute{
path("orders"){
rightPath{ (name:String,order:Order) =>
complete(<H1>Hello ${name}! Welcome to our site! Your order => ${order.toString}</H1>)
}
}
}
我们用curl测试一下
整个service的源码:
import akka.actor.{Props, ActorSystem}
import akka.io.IO
import akka.util.Timeout
import shapeless.Zipper.Put
import spray.can.Http
import spray.routing._
import spray.http._
import spray.routing.authentication.{UserPass, BasicAuth}
import scala.concurrent.duration._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object ActorTest5 extends App{
implicit val system = ActorSystem("mySystem")
val se = system.actorOf(Props[MyService2])
implicit val timeout = Timeout(5.seconds)
IO(Http) ! Http.Bind(se,interface = "localhost",port=8080)
}
case class Order(id:Int)
class MyService2 extends HttpServiceActor {
def authenticator(userPass: Option[UserPass]): Future[Option[String]] = Future {
//检查用户
if (userPass.exists(up => up.user == "admin" && up.pass == "admin")) Some(userPass.get.user)
else None
}
val rightPath = post & authenticate(BasicAuth(authenticator _, realm = "secure site")) & parameters('id.as[Int]).as(Order)
val route = sealRoute{
path("orders"){
rightPath{ (name:String,order:Order) =>
complete(<H1>Hello ${name}! Welcome to our site! Your order => ${order.toString}</H1>)
}
}
}
def receive = runRoute(route)
}
2.构建client端
第一步是提供ActorSystem
implicit val system = ActorSystem()
import system.dispatcher
这里我们使用"管道"的方式请求
A pipeline of type HttpRequest => Future[HttpResponse]
从签名就可以知道大概意思了
val pipeline: HttpRequest => Future[String] = (
addCredentials(BasicHttpCredentials("admin", "admin"))
~> encode(Gzip)
~> sendReceive
~> decode(Deflate)
~> unmarshal[String]
)
这里需要注意的是~>函数的组合 相当于haskell的客串化 >>=
我们看看他是如何实现的
implicit class WithTransformerConcatenation[A, B](f: A ⇒ B) extends (A ⇒ B) {
def apply(input: A) = f(input)
def ~>[AA, BB, R](g: AA ⇒ BB)(implicit aux: TransformerAux[A, B, AA, BB, R]) =
new WithTransformerConcatenation[A, R](aux(f, g))
}
这里使用了隐式转换
将函数转换成WithTransformerConcatenation,主要是添加~>的方法,apply中把函数的入参"处理了"-----f(input) 接着通过~>把结果传递给下一个函数
整个源码:
import akka.actor.ActorSystem
import akka.io.IO
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import spray.json.DefaultJsonProtocol
import spray.httpx.encoding.{Gzip, Deflate}
import spray.httpx.SprayJsonSupport._
import spray.client.pipelining._
import akka.pattern.ask
case class Order1(id: Int)
object JsonConver extends DefaultJsonProtocol {
implicit val orderFormat = jsonFormat1(Order1)
}
object ActorTest1 extends App{
import JsonConver._
implicit val system = ActorSystem()
import system.dispatcher
val pipeline: HttpRequest => Future[String] = (
addCredentials(BasicHttpCredentials("admin", "admin"))
~> encode(Gzip)
~> sendReceive
~> decode(Deflate)
~> unmarshal[String]
)
val response: Future[String] = pipeline(Post("http://localhost:8080/orders?id=2001"))
response.onSuccess{
case res => println(res)
}
}