2017-05-04 1 views
2

J'essaye d'écrire un acteur dans Akka/Scala qui appelle une API HTTP REST et renvoie le résultat à l'acteur appelant. L'API peut renvoyer une collection/un vecteur de résultats qui doit d'abord être converti en un format interne fournisseur neutre afin que le fournisseur puisse être modifié à l'avenir sans beaucoup de changements requis sur notre code. La plupart du code fonctionne mais je ne suis pas sûr de savoir comment décompresser et envoyer le vecteur interne.Retour résultat réel d'un acteur au lieu de promesse/avenir

C'est le code que j'ai et il renvoie un Promise à l'acteur invoquant. Ce que je voudrais revenir est le vecteur réelle qui est créé dans l'opération map finale:

class RESTActor extends Actor with ActorLogging with JsonSupport { 

    final implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system)) 
    val http = Http(context.system) 

    import akka.pattern.pipe 
    import context.dispatcher 

    override def receive: Receive = { 
    case query: String => { 
     val requester = sender 
     var uri = Uri(Settings.autoCompleteURL).withQuery(Query(Map("query" -> query))) 

     sender! http 
     .singleRequest(HttpRequest(HttpMethods.GET, uri = uri)) 
     .flatMap(response => 
      response.status match { 
      case status if status.isSuccess() => Unmarshal(response.entity).to[VendorResponse].map(_.result.map(x => VendorNeutralResponse(x.id, x.field))) 

      case _ => response.entity.toStrict(5.seconds).map { entity => 
       val body = entity.data.decodeString("UTF-8") 
       log.warning(errorMessage(response, body)) 
       Left(errorMessage(response, body)) 
      } 

      }) 
    } 

    } 

L'acteur appelant:

pathPrefix("search") { 
      get { 
      parameter("query") { query => 
       { 
       complete(restActor.ask(query)) //Doesn't work as the reply is a promise 

       } 
      } 
      } 
     } 

Comment puis-je modifier le code ci-dessus dans le RESTActor à envoyer réelle résultat au lieu d'un avenir ou d'une promesse?

Modifier: Après avoir modifié le code en fonction de mes propres recherches et suggestions de @ Michał et @ cyrille-Corpet, le code suivant fonctionne:

pathPrefix("search") { 
      get { 
      parameter("query") { query => 
       { 
       onComplete(gisRouter.ask(query)) { 
        case Success(resp: Future[Vector[VendorNeutralResponse]]) => { 
        resp.map(println) 
        complete("ok") 
        } 
        case Failure(e) => complete(e.toString) 
       } 

       } 
      } 
      } 
     } 

Il semble que je suis toujours obtenir un future en réponse de mon acteur. Comment puis-je obtenir l'acteur de répondre avec des données réelles et non un Future?

Répondre

1

Vous pouvez utiliser la fonction onComplete, qui prend une Future[T] en entrée, et retourne un Directive1[Try[T]], de sorte que vous pouvez utiliser comme suivant:

pathPrefix("search") { 
    get { 
    parameter("query") { query => 
     onComplete(restActor.ask(query)) { 
     case Success(resp) => ... 
     case Failure(e) => ... 
     } 
    } 
    } 
} 

EDIT sur le retour de votre acteur, vous devriez tuyau le résultat de http.singleRequest à l'expéditeur, au lieu de dire qu'il:

http.singleRequest(...).flatMap(...) pipeTo requester 

de cette façon, tell réelle (!) sera don e seulement une fois que le Future est résolu.

+0

Salut..le code n'aide pas. Je dois encore déballer un futur à l'intérieur sur Complet..veuillez vous référer à la mise à jour/Modifier. – MojoJojo

+0

@MojoJojo la modification à la réponse vous suggère de transmettre 'Future' à l'expéditeur au lieu de retourner Future dans le cadre de la réponse – dk14

+0

@ dk14 L'édition a été faite à cause de ce commentaire ... –

1

Vous pouvez le faire monadique comme en utilisant la carte:

pathPrefix("search") { 
    get { 
    parameter("query") { query => 
     restActor.ask(query) map { 
     case Success(resp) => ... 
     case Failure(e) => ... 
     } 
    } 
    } 
} 

EDIT: Actuellement, votre Acteur répond avec l'avenir. Essayez de le refactoriser pour qu'il renvoie la valeur non dépliée à la place:

class RESTActor extends Actor with ActorLogging with JsonSupport { 

    final implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system)) 
    val http = Http(context.system) 

    import akka.pattern.pipe 
    import context.dispatcher 

    private def handleHttpResponse = { 
    case status if status.isSuccess() => Unmarshal(response.entity).to[VendorResponse].map(_.result.map(x => VendorNeutralResponse(x.id, x.field))) 

    case _ => response.entity.toStrict(5.seconds).map { entity => 
     val body = entity.data.decodeString("UTF-8") 
     log.warning(errorMessage(response, body)) 
     Left(errorMessage(response, body)) 
    } 

    } 

    override def receive: Receive = { 
    case query: String => { 
     val requester = sender 
     var uri = Uri(Settings.autoCompleteURL).withQuery(Query(Map("query" -> query))) 

     http.singleRequest(HttpRequest(HttpMethods.GET, uri = uri)).flatMap(response => 
      response.status map handleHttpResponse) pipeTo requester 
    } 

    } 
+0

Merci pour la réponse ... le code n'aide pas - je reçois encore un avenir de l'acteur et dois mapper/flatmap à nouveau..avoir mis à jour la question avec le code. – MojoJojo

+0

@MojoJojo S'il vous plaît se référer à la modification ci-dessus –