Scala - making a WS http request

1.4k Views Asked by At

UPDATED: Method is returning type ANY rather than type Future[string]. Require return of type String.

I'm making a http request using the play.ws library 2.6. This was previously done with a curl request but this only uses basic authentication.

Below is my code and I'm trying to return a json string from this function to be deserialised in another method.

import java.io.{File, InputStream}
import java.nio.file.Paths
import javax.inject._

import org.apache.commons.io.FileUtils

import play.api._
import play.api.http.HttpEntity
import play.api.libs.ws._
import play.api.mvc._
import play.api.Play.current

import scala.collection.mutable.ListBuffer
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

import sys.process._

@Singleton
class BuildService @Inject() (
ws: WSClient,
ec: ExecutionContext,
config: Configuration) {

    def bbApiRequest(requestUrl: String, timeout: FiniteDuration): 
        Future[String] = {
        val request = ws
            .url(requestUrl)
            .withAuth(
                "user", 
                "pw1234",
                WSAuthScheme.BASIC)
            .get()
        Await.result(request, timeout)
        val returner = request.map(_.json)
    } // <-- line 72 in below error points here.
} 

When run it produces the error:

[error] C:\my_path\app\services\BuildService.scala:72: type mismatch;
[error]  found   : Unit
[error]  required: scala.concurrent.Future[String]
[error]         }
[error]         ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[info] Compiling 1 Scala source to C:\my_path\restapi\target\scala-
       2.12\classes...
[error] C:\my_path\restapi\app\services\BuildService.scala:72: type 
        mismatch;
[error]  found   : Unit
[error]  required: scala.concurrent.Future[String]
[error]         }
[error]         ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed

I'm trying to get ideally:

  • A string return (case class and json method to unpack a string)
  • Synchronous request (if async I'll need to wait to complete to progress application)
  • Secure (allows use of tokens for verification)

Appreciate any help!

2

There are 2 best solutions below

3
pme On

Here is the function I use:

// first work with Futures the Play Controller will support that!
def bbApiRequest(requestUrl: String): Future[String] = {

  // call the webservice with basic authentication
  wsClient.url(requestUrl)
    .withAuth("tester", "pwd123", WSAuthScheme.BASIC)
    .get()
    .map(checkStatus) // function that evaluates the HTTP Status
    .map(_.json) // get the json
    .map(Json.prettyPrint) // get it as string

}

I would create a case class directly like: .map(jsValue => handleJson(jsValue.validate[YourModel])) // or create a model of it (instead) instead of .map(Json.prettyPrint)

Edit

Here an example of checkStatus:

  protected def checkStatus(resp: WSResponse): WSResponse = {
    resp.status match {
      case Status.OK => resp
      case Status.NOT_FOUND => throw WebNotFoundException()
      case Status.FORBIDDEN | Status.UNAUTHORIZED => throw WebAccessForbiddenException()
      case Status.NOT_ACCEPTABLE => throw WebNotAcceptableException()
      case _ => throw WebBadStatusException(resp.status + " - " + resp.statusText.toString)
    }
  }

The Exception are created by myself.

0
crowgers On

The authorization can also be inputs into the function or retrieved from environment variables within the method (much easier to manage).

Simply needed to use .body on the Await call, which converts the output to generic type string.

package utils

import javax.inject._

import play.api._
import play.api.http.HttpEntity
import play.api.libs.ws._
import play.api.mvc._
import play.api.Play.current

import scala.collection.mutable.ListBuffer
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

import sys.process._

@Singleton
class HTTPRequest @Inject() (
ws: WSClient,
ec: ExecutionContext,
config: Configuration) {

    def bbApiRequest(requestUrl: String, timeout: FiniteDuration) = {
        val request = ws
            .url(requestUrl)
            .withAuth(
                "user", 
                "PW123", 
                WSAuthScheme.BASIC)
            .get()
        Await.result(request, timeout).body
    }
}