The HttpExt
class has a method bindAndHAndleAsync
that can be used for this purpose. This method takes in a function with the following signature:
handler: (HttpRequest) ⇒ Future[HttpResponse]
So, suppose we have an Actor that will produce an HttpResponse
when asked about an HttpRequest
:
class HttpResponseHandlerActor extends Actor {
override def receive = {
case _ : HttpRequest =>
sender() ! HttpResponse(200, entity = "Response From Actor")
}
}
Inefficient Answer
Your question explicitly asks how to use 1 Actor per request, to do that we can now use our Actor class to create a handler function:
implicit val actorSystem = ActorSystem()
implicit val timeout = Timeout(5 seconds)
val handler : (HttpRequest) => Future[HttpResponse] = (httpRequest) = {
val actorHandlerRef =
system.actorOf(Props[HttpResponseHandlerActor], "responseActor")
(actorHandlerRef ask httpRequest).mapTo[HttpResponse]
}
We can now use this function to bind our server with:
val serverBinding : Future[ServerBinding] =
Http().bindAndHandleAsync(handler, "localhost", 8080)
Efficient Answer
It is usually not necessary to re-create a new Actor for each request, typically you want to create 1 Actor and use it for every request.
Therefore we can move the Actor creation outside of handler
:
val handler : (ActorRef) => (HttpRequest) => Future[HttpResponse] =
(actorRef) => (httpRequest) =>
(actorRef ask httpRequest).mapTo[HttpResponse]
The server binding is now slightly modified to:
val singleResponseActorRef =
system.actorOf(Props[HttpResponseHandlerActor], "responseActor")
val serverBinding : Future[ServerBinding] =
Http().bindAndHandleAsync(handler(singleResponseActorRef),
"localhost",
8080)