skip to Main Content

I built an Akka-Http Client inside a Server, which gets a GET request by a browser and then uses it’s one parameter to make a POST request to Twitter. Then, the server makes a JSON string out of the Twitter’s response and gives it back to the browser.

This image describes this simple interaction

So, my code works. It does. The problem is it works only once. If I call GET /StepOne for a second time, it fails.

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))
      })
    }
  }
}

So as you can see, I declare a var called JsonRSP which initially prints “null string”. After that I make the POST call and I edit the JsonRSP with the information given by Twitter. The second time I call this StepOne API, the JsonRSP string isn’t edited and it prints the “null string” over again. This happens despite the fact that Twitter gives me the right information every time.

So, for example, the first time I get:

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

But the second time I call my API it returns:

“Null String”

Any ideas? I’m well known for making obvious mistakes so please help me !

________ EDIT ______

I’ve edited the code to get rid of an aux variable that wasn’t needed at all.
So the main thing here is that if I print jsonRSP inside de runForeach it prints the correct JSON. But when I print it before doing the complete it prints “null string” all over again. To make it weirder, it still completes the right json the first time, but “null string” the second time. So I don’t have a clue why is this happening. If my code shoudln’t work, it shouldn’t work everytime, so why does it work the first time?

2

Answers


  1. Chosen as BEST ANSWER

    Finally I chose what is cleaner. Thanks to @tea-adict 's response I came up with this code.

    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"))
            }
    
          })
        }
      }
    }
    

    It doesn't complete a JSON string. Instead, it sends the same plain-text gotten from Twitter back to the front-end app. Then, I will simply take that response in my front-end and transform it to a JSON string to parse it and work with it.

    It's much cleaner this way.


  2. I think your problem is related with Future, because you don’t wait for response actually, complete(HttpEntity(ContentTypes.'application/json', jsonRSP)), you always return jsonRSP. You should wait for Future computation and then return it.

    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))
            })
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search