请看
spray-client library。它提供了HTTP请求功能(我假设您要通信的服务器是一个网络服务?)。它为构建请求提供了相当不错的DSL,并且全部都是关于异步操作的。它在幕后确实使用了akka Actor模型,但您不必构建自己的Actor来使用它。相反,您可以只使用Scala的Future模型来异步处理事物。有关Future模型的良好介绍可以在
这里找到。
spray-client的基本构建块是“pipeline”,它将HttpRequest映射到包含HttpResponse的Future中。
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
val response: Future[HttpResponse] = pipeline(Get("http://spray.io/"))
您可以将这个基本构建块拿来,在几个步骤中构建出一个客户端 API。首先,创建一个类来设置管道并定义一些中间帮助程序,演示
响应转换 技术。
import scala.concurrent._
import spray.can.client.HttpClient
import spray.client.HttpConduit
import spray.client.HttpConduit._
import spray.http.{HttpRequest, HttpResponse, FormData}
import spray.httpx.unmarshalling.Unmarshaller
import spray.io.IOExtension
type Pipeline = (HttpRequest) => Future[HttpResponse]
def createPipeline(system: ActorSystem, host: String, port: Int): Pipeline = {
val httpClient = system.actorOf(Props(new HttpClient(IOExtension(system).ioBridge())))
val conduit = system.actorOf(props = Props(new HttpConduit(httpClient, host, port)))
sendReceive(conduit)
}
private var pipeline: Pipeline = _
private def unmarshallingPipeline[T](implicit ec:ExecutionContext, um:Unmarshaller[T]) = (pipeline ~> unmarshal[T])
private def unitPipeline(implicit ec:ExecutionContext) = (pipeline ~> { _:HttpResponse => () })
private def statusPipeline(implicit ec:ExecutionContext) = (pipeline -> {r:HttpResponse => r.status})
val errorFilter = { response:HttpResponse =>
if(response.status.isSuccess) response
else if(response.status.value >= 500) throw RemoteServerError(response)
else throw RemoteClientError(response)
}
pipeline = (createPipeline(system, "yourHost", 8080) ~> errorFilter)
然后,您可以将这些方法封装到与特定请求/响应相关联的方法中,从而成为公共API。例如,假设服务具有“ping” GET端点,返回一个字符串(“pong”),以及一个“form” POST端点,您在其中发布表单数据并收到一个DataModel作为回应:
def ping()(implicit ec:ExecutionContext, um:Unmarshaller[String]): Future[String] =
unmarshallingPipeline(Get("/ping"))
def form(formData: Map[String, String])(implicit ec:ExecutionContext, um:Unmarshaller[DataModel]): Future[DataModel] =
unmarshallingPipeline(Post("/form"), FormData(formData))
然后有人可以像这样使用API:
import scala.util.{Failure, Success}
API.ping() foreach(println)
API.form(Map("a" -> "b") onComplete {
case Success(dataModel) => println("Form accepted. Server returned DataModel: " + dataModel)
case Failure(e) => println("Oh noes, the form didn't go through! " + e)
}
我不确定您是否能在spray-client中直接获得有关订阅事件的第三个要点的支持。这些事件是否由服务器生成并以某种方式发送到客户端,超出了特定HTTP请求的范围?如果是这样,那么spray-client可能无法直接帮助您(尽管您的事件处理程序仍然可以使用它来发送请求)。事件是否发生在客户端,例如最初由服务器响应触发的延迟处理完成?如果是这样,您实际上可能只需要使用Future中的功能就可以取得很大进展,但根据您的用例,使用Actors可能是有意义的。