Search code examples
javaakka-streamakka-http

How to respond with the result of an actor call?


We are looking at using Akka-HTTP Java API - using Routing DSL.

It's not clear how to use the Routing functionality to respond to an HttpRequest; using an Untyped Akka Actor. For example, upon matching a Route path, how do we hand off the request to a "handler" ActorRef, which will then respond with a HttpResponse in a asynchronous way?

A similar question was posted on Akka-User mailing list, but with no followup solutions as such - https://groups.google.com/d/msg/akka-user/qHe3Ko7EVvg/KC-aKz_o5aoJ.


Solution

  • This can be accomplished with a combination of the onComplete directive and the ask pattern.

    In the below example the RequestHandlerActor actor is used to create a HttpResponse based on the HttpRequest. This Actor is asked from within the route.

    I have never used Java for routing code so my response is in Scala.

    import scala.concurrent.duration._
    import akka.actor.ActorSystem
    import akka.http.scaladsl.model.HttpResponse
    import akka.http.scaladsl.model.HttpRequest
    import akka.actor.Actor
    import akka.http.scaladsl.server.Directives._
    import akka.actor.Props
    import akka.pattern.ask
    import akka.util.Timeout
    import scala.util.{Success, Failure}
    import akka.http.scaladsl.model.StatusCodes.InternalServerError
    
    class RequestHandlerActor extends Actor {
      override def receive = {
        case httpRequest : HttpRequest =>
          sender() ! HttpResponse(entity = "actor responds nicely")
      }
    }
    
    implicit val actorSystem = ActorSystem()
    implicit val timeout = Timeout(5 seconds)
    
    val requestRef = actorSystem actorOf Props[RequestHandlerActor]
    
    val route = 
      extractRequest { request =>
        onComplete((requestRef ? request).mapTo[HttpResponse]) {
          case Success(response) => complete(response)
          case Failure(ex) => 
            complete((InternalServerError, s"Actor not playing nice: ${ex.getMessage}"))
        } 
      }
    

    This route can then be used passed into the bindAndHandle method like any other Flow.