AkkaHttp - 在服务器内建立客户端

时间:2018-05-20 00:49:50

标签: scala akka-http

我在服务器内部构建了一个Akka-Http客户端,它通过浏览器获取GET请求,然后使用它的一个参数向Twitter发出POST请求。然后,服务器从Twitter的响应中生成一个JSON字符串,并将其返回给浏览器。

This image describes this simple interaction

所以,我的代码有效。确实如此。问题是它只能工作一次。如果我第二次打电话给GET / StepOne,它就会失败。

val route = cors(settings){
  path("StepOne"){
    get {
      parameters('callback.as[String])(cb => {
        val callback = this.encodeUriComp(cb)
        val url = "https://api.twitter.com/oauth/request_token"
        var response: String = null
        this.oauth_timestamp = this.createTimestamp()
        this.oauth_nonce = this.randomString(32)
        val authorization = headers.RawHeader("Authorization",
          """OAuth oauth_callback="""" + callback +
            """", oauth_consumer_key="""" + this.consumerKey +
            """", oauth_nonce="""" + this.oauth_nonce +
            """", oauth_signature="""" + this.encodeUriComp(this.createSignature()) +
            """", oauth_signature_method="HMAC-SHA1", oauth_timestamp="""" + this.oauth_timestamp +
            """", oauth_version="1.0"""")
        val params = ByteString(callback)
        var jsonRSP = "null string"
        val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(HttpMethods.POST, url,
          headers = List(authorization),
          entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, params)))
        responseFuture
          .onComplete {
            case Success(res) => {
              val response = res._3.dataBytes.map(_.utf8String).runForeach(body => {
                /* We postedit the string to make it JSON Parsable */
                 jsonRSP = "{" + body.replaceAll("=", ":")
                                     .replaceAll("&", ",")
                                     .replaceAll("([\\w-]+)", "\"$1\"") + "}"
               // jsonRSP = postBody
                println(jsonRSP)

              })
            }
            case Failure(_) => sys.error("Couldn't get into api.twitter")
          } 
        implicit val timeout = Timeout(5, TimeUnit.SECONDS)
        Await.result(responseFuture, timeout.duration)
        println(jsonRSP)
        complete(HttpEntity(ContentTypes.`application/json`, jsonRSP))
      })
    }
  }
}

正如您所看到的,我声明了一个名为JsonRSP的var,它最初打印“null string”。之后,我进行POST调用,并使用Twitter提供的信息编辑JsonRSP。我第二次调用这个StepOne API时,JsonRSP字符串不会被编辑,它会再次打印“null string”。尽管Twitter每次都给我正确的信息,但仍然会发生这种情况。

所以,例如,我第一次得到:

{"oauth_token":"mytoken","oauth_token_secret":"mysecrettoken","oauth_callback_confirmed":"true"}

但是我第二次调用我的API时会返回:

“空字符串”

有什么想法吗?我因犯明显错误而闻名,所以请帮助我!

________编辑______

我编辑了代码以摆脱根本不需要的辅助变量。 所以这里的主要内容是如果我在de runForeach中打印jsonRSP,它会输出正确的JSON。但是当我在完成之前打印它时,它会再次打印“null string”。为了使它更奇怪,它仍然第一次完成正确的json,但第二次是“null string”。所以我不知道为什么会这样。如果我的代码不能正常工作,那么它每次都不应该工作,那为什么它第一次工作呢?

2 个答案:

答案 0 :(得分:1)

我认为您的问题与Future有关,因为您不等待实际回复,complete(HttpEntity(ContentTypes.'application/json', jsonRSP)),您总是返回jsonRSP。您应该等待Future计算,然后将其返回。

val route = cors(settings){
    path("StepOne"){
        get {
        parameters('callback.as[String])(cb => {
            val callback = this.encodeUriComp(cb)
            val url = "https://api.twitter.com/oauth/request_token"
            var response: String = null
            this.oauth_timestamp = this.createTimestamp()
            this.oauth_nonce = this.randomString(32)
            val authorization = headers.RawHeader("Authorization",
            """OAuth oauth_callback="""" + callback +
                """", oauth_consumer_key="""" + this.consumerKey +
                """", oauth_nonce="""" + this.oauth_nonce +
                """", oauth_signature="""" + this.encodeUriComp(this.createSignature()) +
                """", oauth_signature_method="HMAC-SHA1", oauth_timestamp="""" + this.oauth_timestamp +
                """", oauth_version="1.0"""")
            val params = ByteString(callback)
            val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(HttpMethods.POST, url,
            headers = List(authorization),
            entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, params)))
            responseFuture
            .onComplete {
                case Success(res) => {
                res._3.dataBytes.map(_.utf8String).runForeach(body => {
                    /* We postedit the string to make it JSON Parsable */
                    "{" + body.replaceAll("=", ":")
                                        .replaceAll("&", ",")
                                        .replaceAll("([\\w-]+)", "\"$1\"") + "}"
                })
                }
                case Failure(_) => sys.error("Couldn't get into api.twitter")
            } 
            implicit val timeout = Timeout(5, TimeUnit.SECONDS)
            val result = Await.result(responseFuture, timeout.duration)
            complete(HttpEntity(ContentTypes.`application/json`, result))
        })
        }
    }
}

答案 1 :(得分:1)

最后我选择了更干净的东西。感谢@ tea-adict的回复,我想出了这段代码。

val settings = CorsSettings.defaultSettings.withAllowCredentials(false)
val route = cors(settings){
  path("StepOne"){
    get {
      parameters('callback.as[String])(cb => {
        val callback = this.encodeUriComp(cb)
        val url = "https://api.twitter.com/oauth/request_token"
        this.oauth_timestamp = this.createTimestamp()
        this.oauth_nonce = this.randomString(32)
        val authorization = headers.RawHeader("Authorization",
          """OAuth oauth_callback="""" + callback +
            """", oauth_consumer_key="""" + this.consumerKey +
            """", oauth_nonce="""" + this.oauth_nonce +
            """", oauth_signature="""" + this.encodeUriComp(this.createSignature()) +
            """", oauth_signature_method="HMAC-SHA1", oauth_timestamp="""" + this.oauth_timestamp +
            """", oauth_version="1.0"""")
        val params = ByteString(callback)


        val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(HttpMethods.POST, url,
          headers = List(authorization),
          entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, params)))


        implicit val timeout = Timeout(5, TimeUnit.SECONDS)
        try {
          val result = Await.result(responseFuture, timeout.duration)
          complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`,result.entity.dataBytes))
        }
        catch{
          case e: TimeoutException => complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`,"oauth_token=null&oauth_token_secret=null&callback_confirmed=false"))
        }

      })
    }
  }
}

它没有完成JSON字符串。相反,它将从Twitter获得的相同纯文本发送回前端应用程序。然后,我将简单地在前端获取该响应并将其转换为JSON字符串以解析它并使用它。

这种方式更干净。