alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Play Framework/Scala example source code file (WS.scala)

This example Play Framework source code file (WS.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Play Framework (and Scala) source code examples by using tags.

All credit for the original source code belongs to Play Framework; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Play Framework tags/keywords

a, api, concurrent, future, lib, library, map, option, play, play framework, seq, string, t, wsauthscheme, wsrequest, wsrequestholder

The WS.scala Play Framework example source code

/*
 * Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
 */
package play.api.libs.ws

import scala.concurrent.{ Future, ExecutionContext }

import java.io.File

import play.api.http.{ Writeable, ContentTypeOf }
import play.api.libs.iteratee._

import play.api._
import scala.xml.Elem
import play.api.libs.json.JsValue

/**
 * The WSClient holds the configuration information needed to build a request, and provides a way to get a request holder.
 */
trait WSClient {

  /**
   * The underlying implementation of the client, if any.  You must cast explicitly to the type you want.
   * @tparam T the type you are expecting (i.e. isInstanceOf)
   * @return the backing class.
   */
  def underlying[T]: T

  /**
   * Generates a request holder which can be used to build requests.
   *
   * @param url The base URL to make HTTP requests to.
   * @return a WSRequestHolder
   */
  def url(url: String): WSRequestHolder
}

/**
 * WSRequestHolderMagnet magnet.  Please see the companion object for implicit definitions.
 *
 * @see <a href="http://spray.io/blog/2012-12-13-the-magnet-pattern/">The magnet pattern</a>
 */
trait WSRequestHolderMagnet {
  def apply(): WSRequestHolder
}

/**
 * Asynchronous API to to query web services, as an http client.
 *
 * Usage example:
 * {{{
 * WS.url("http://example.com/feed").get()
 * WS.url("http://example.com/item").post("content")
 * }}}
 *
 * When greater flexibility is needed, you can also create clients explicitly and pass them into WS:
 *
 * {{{
 * implicit val client = new NingWSClient(builder.build())
 * WS.url("http://example.com/feed").get()
 * }}}
 *
 * Or call the client directly:
 *
 * {{{
 * import com.typesafe.config.ConfigFactory
 * import play.api.libs.ws._
 * import play.api.libs.ws.ning._
 *
 * val configuration = play.api.Configuration(ConfigFactory.parseString(
 * """
 *   |ws.ssl.trustManager = ...
 * """.stripMargin))
 * val parser = new DefaultWSConfigParser(configuration, Play.current.classloader)
 * val builder = new NingAsyncHttpClientConfigBuilder(parser.parse())
 * val secureClient : WSClient = new NingWSClient(builder.build())
 * val response = secureClient.url("https://secure.com").get()
 * }}}
 *
 * Note that the resolution of URL is done through the magnet pattern defined in
 * `WSRequestHolderMagnet`.
 *
 * The value returned is a {@code Future[WSResponse]}, and you should use Play's asynchronous mechanisms to
 * use this response.
 */
object WS {

  @deprecated("Please use play.api.libs.ws.WSRequest", "2.3.0")
  type WSRequest = play.api.libs.ws.WSRequest

  @deprecated("Please use play.api.libs.ws.WSResponse", "2.3.0")
  type Response = play.api.libs.ws.WSResponse

  @deprecated("Please use play.api.libs.ws.WSRequestHolder", "2.3.0")
  type WSRequestHolder = play.api.libs.ws.WSRequestHolder

  protected[play] def wsapi(implicit app: Application): WSAPI = {
    app.plugin[WSPlugin] match {
      case Some(plugin) => plugin.api
      case None => throw new Exception("There is no WS plugin registered.")
    }
  }

  import scala.language.implicitConversions

  /**
   * Retrieves or creates underlying HTTP client.  Note that due to the Plugin architecture, an
   * implicit application must be in scope.  Most of the time you will want the current app:
   *
   * {{{
   * import play.api.Play.current
   * val client = WS.client
   * }}}
   */
  def client(implicit app: Application): WSClient = wsapi.client

  /**
   * Prepares a new request using an implicit application.  This creates a default client, which you can then
   * use to construct a request.
   *
   * {{{
   *   import play.api.Play.current
   *   WS.url("http://localhost/").get()
   * }}}
   *
   * @param url the URL to request
   * @param app the implicit application to use.
   */
  def url(url: String)(implicit app: Application): play.api.libs.ws.WSRequestHolder = wsapi(app).url(url)

  /**
   * Prepares a new request using a provided magnet.  This method gives you the ability to create your own
   * URL implementations at the cost of some complexity.
   *
   * {{{
   * object PairMagnet {
   *   implicit def fromPair(pair: Pair[WSClient, java.net.URL]) =
   *     new WSRequestHolderMagnet {
   *       def apply(): WSRequestHolder = {
   *         val (client, netUrl) = pair
   *         client.url(netUrl.toString)
   *       }
   *    }
   * }
   * import scala.language.implicitConversions
   * val client = WS.client
   * val exampleURL = new java.net.URL("http://example.com")
   * WS.url(client -> exampleURL).get()
   * }}}
   *
   * @param magnet a magnet pattern.
   * @see <a href="http://spray.io/blog/2012-12-13-the-magnet-pattern/">The magnet pattern</a>
   */
  def url(magnet: WSRequestHolderMagnet): play.api.libs.ws.WSRequestHolder = magnet()

  /**
   * Prepares a new request using an implicit client.  The client must be in scope and configured, i.e.
   *
   * {{{
   * implicit val sslClient = new play.api.libs.ws.ning.NingWSClient(sslBuilder.build())
   * WS.clientUrl("http://example.com/feed")
   * }}}
   *
   * @param url the URL to request
   * @param client the client to use to make the request.
   */
  def clientUrl(url: String)(implicit client: WSClient): play.api.libs.ws.WSRequestHolder = client.url(url)
}

/**
 * The base WS API trait.  Plugins should extend this.
 */
trait WSAPI {

  def client: WSClient

  def url(url: String): WSRequestHolder
}

/**
 * The WS plugin.
 */
abstract class WSPlugin extends Plugin {

  def api: WSAPI

  private[ws] def loaded: Boolean
}

/**
 * WSRequest is used internally.  Please use WSRequestHolder.
 */
trait WSRequest {

  /**
   * Return the current headers of the request being constructed
   */
  def allHeaders: Map[String, Seq[String]]

  /**
   * Return the current query string parameters
   */
  def queryString: Map[String, Seq[String]]

  /**
   * Retrieve an HTTP header.
   */
  def header(name: String): Option[String]

  /**
   * The HTTP method.
   */
  def method: String

  /**
   * The URL
   */
  def url: String

  /**
   * Get the body.
   *
   * Will only return the body if the body exists in memory, will not return it if it's a stream.
   */
  def getBody: Option[Array[Byte]]

  /**
   * Set an HTTP header.
   */
  @scala.deprecated("Use WSRequestHolder", "2.3.0")
  def setHeader(name: String, value: String): WSRequest

  /**
   * Add an HTTP header (used for headers with multiple values).
   */
  @scala.deprecated("Use WSRequestHolder", "2.3.0")
  def addHeader(name: String, value: String): WSRequest

  //@scala.deprecated
  //override def setHeaders(hdrs: FluentCaseInsensitiveStringsMap)

  /**
   * Defines the request headers.
   */
  @scala.deprecated("Use WSRequestHolder", "2.3.0")
  def setHeaders(hdrs: java.util.Map[String, java.util.Collection[String]]): WSRequest

  /**
   * Defines the request headers.
   */
  @scala.deprecated("Use WSRequestHolder", "2.3.0")
  def setHeaders(hdrs: Map[String, Seq[String]]): WSRequest

  /**
   * Defines the query string.
   */
  @scala.deprecated("Use WSRequestHolder", "2.3.0")
  def setQueryString(queryString: Map[String, Seq[String]]): WSRequest

  @scala.deprecated("Use WSRequestHolder", "2.3.0")
  def setUrl(url: String): WSRequest
}

/**
 *
 */
trait WSResponse {

  /**
   * Return the current headers of the request being constructed
   */
  def allHeaders: Map[String, Seq[String]]

  /**
   * Get the underlying response object.
   */
  def underlying[T]: T

  /**
   * The response status code.
   */
  def status: Int

  /**
   * The response status message.
   */
  def statusText: String

  /**
   * Get a response header.
   */
  def header(key: String): Option[String]

  /**
   * Get all the cookies.
   */
  def cookies: Seq[WSCookie]

  /**
   * Get only one cookie, using the cookie name.
   */
  def cookie(name: String): Option[WSCookie]

  /**
   * The response body as String.
   */
  def body: String

  /**
   * The response body as Xml.
   */
  def xml: Elem

  /**
   * The response body as Json.
   */
  def json: JsValue

}

/**
 * A body for the request
 */
sealed trait WSBody

/**
 * An in memory body
 *
 * @param bytes The bytes of the body
 */
case class InMemoryBody(bytes: Array[Byte]) extends WSBody

/**
 * A streamed body
 *
 * @param bytes An enumerator of the bytes of the body
 */
case class StreamedBody(bytes: Enumerator[Array[Byte]]) extends WSBody {
  throw new NotImplementedError("A streaming request body is not yet implemented")
}

/**
 * A file body
 */
case class FileBody(file: File) extends WSBody

/**
 * An empty body
 */
case object EmptyBody extends WSBody

/**
 * A WS Request builder.
 */
trait WSRequestHolder {

  /**
   * The URL for this request
   */
  val url: String

  /**
   * The method for this request
   */
  val method: String

  /**
   * The body of this request
   */
  val body: WSBody

  /**
   * The headers for this request
   */
  val headers: Map[String, Seq[String]]

  /**
   * The query string for this request
   */
  val queryString: Map[String, Seq[String]]

  /**
   * A calculator of the signature for this request
   */
  val calc: Option[WSSignatureCalculator]

  /**
   * The authentication this request should use
   */
  val auth: Option[(String, String, WSAuthScheme)]

  /**
   * Whether this request should follow redirects
   */
  val followRedirects: Option[Boolean]

  /**
   * The timeout for the request
   */
  val requestTimeout: Option[Int]

  /**
   * The virtual host this request will use
   */
  val virtualHost: Option[String]

  /**
   * The proxy server this request will use
   */
  val proxyServer: Option[WSProxyServer]

  /**
   * sets the signature calculator for the request
   * @param calc
   */
  def sign(calc: WSSignatureCalculator): WSRequestHolder

  /**
   * sets the authentication realm
   */
  def withAuth(username: String, password: String, scheme: WSAuthScheme): WSRequestHolder

  /**
   * adds any number of HTTP headers
   * @param hdrs
   */
  def withHeaders(hdrs: (String, String)*): WSRequestHolder

  /**
   * adds any number of query string parameters to the
   */
  def withQueryString(parameters: (String, String)*): WSRequestHolder

  /**
   * Sets whether redirects (301, 302) should be followed automatically
   */
  def withFollowRedirects(follow: Boolean): WSRequestHolder

  @scala.deprecated("use withRequestTimeout instead", "2.1.0")
  def withTimeout(timeout: Int) = withRequestTimeout(timeout)

  /**
   * Sets the maximum time in millisecond you accept the request to take.
   * Warning: a stream consumption will be interrupted when this time is reached.
   */
  def withRequestTimeout(timeout: Int): WSRequestHolder

  /**
   * Sets the virtual host to use in this request
   */
  def withVirtualHost(vh: String): WSRequestHolder

  /**
   * Sets the proxy server to use in this request
   */
  def withProxyServer(proxyServer: WSProxyServer): WSRequestHolder

  /**
   * Sets the body for this request
   */
  def withBody(body: WSBody): WSRequestHolder

  /**
   * Sets the body for this request
   */
  def withBody[T](body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]): WSRequestHolder = {
    val wsBody = InMemoryBody(wrt.transform(body))
    if (headers.contains("Content-Type")) {
      withBody(wsBody)
    } else {
      ct.mimeType.fold(withBody(wsBody)) { contentType =>
        withBody(wsBody).withHeaders("Content-Type" -> contentType)
      }
    }
  }

  /**
   * Sets the method for this request
   */
  def withMethod(method: String): WSRequestHolder

  /**
   * performs a get
   */
  def get() = withMethod("GET").execute()

  /**
   * performs a get
   * @param consumer that's handling the response
   */
  def get[A](consumer: WSResponseHeaders => Iteratee[Array[Byte], A])(implicit ec: ExecutionContext): Future[Iteratee[Array[Byte], A]] = {
    getStream().flatMap {
      case (response, enumerator) =>
        enumerator(consumer(response))
    }
  }

  /**
   * performs a get
   */
  def getStream(): Future[(WSResponseHeaders, Enumerator[Array[Byte]])] = {
    withMethod("GET").stream()
  }

  /**
   * Perform a PATCH on the request asynchronously.
   */
  def patch[T](body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]) =
    withMethod("PATCH").withBody(body).execute()

  /**
   * Perform a PATCH on the request asynchronously.
   * Request body won't be chunked
   */
  def patch(body: File) = withMethod("PATCH").withBody(FileBody(body)).execute()

  /**
   * performs a POST with supplied body
   * @param consumer that's handling the response
   */
  def patchAndRetrieveStream[A, T](body: T)(consumer: WSResponseHeaders => Iteratee[Array[Byte], A])(implicit wrt: Writeable[T], ct: ContentTypeOf[T], ec: ExecutionContext): Future[Iteratee[Array[Byte], A]] = {
    withMethod("PATCH").withBody(body).stream().flatMap {
      case (response, enumerator) =>
        enumerator(consumer(response))
    }
  }

  /**
   * Perform a POST on the request asynchronously.
   */
  def post[T](body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]) =
    withMethod("POST").withBody(body).execute()

  /**
   * Perform a POST on the request asynchronously.
   * Request body won't be chunked
   */
  def post(body: File) = withMethod("POST").withBody(FileBody(body)).execute()

  /**
   * performs a POST with supplied body
   * @param consumer that's handling the response
   */
  def postAndRetrieveStream[A, T](body: T)(consumer: WSResponseHeaders => Iteratee[Array[Byte], A])(implicit wrt: Writeable[T], ct: ContentTypeOf[T], ec: ExecutionContext): Future[Iteratee[Array[Byte], A]] = {
    withMethod("POST").withBody(body).stream().flatMap {
      case (response, enumerator) =>
        enumerator(consumer(response))
    }
  }

  /**
   * Perform a PUT on the request asynchronously.
   */
  def put[T](body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]) =
    withMethod("PUT").withBody(body).execute()

  /**
   * Perform a PUT on the request asynchronously.
   * Request body won't be chunked
   */
  def put(body: File) = withMethod("PUT").withBody(FileBody(body)).execute()

  /**
   * performs a PUT with supplied body
   * @param consumer that's handling the response
   */
  def putAndRetrieveStream[A, T](body: T)(consumer: WSResponseHeaders => Iteratee[Array[Byte], A])(implicit wrt: Writeable[T], ct: ContentTypeOf[T], ec: ExecutionContext): Future[Iteratee[Array[Byte], A]] = {
    withMethod("PUT").withBody(body).stream().flatMap {
      case (response, enumerator) =>
        enumerator(consumer(response))
    }
  }

  /**
   * Perform a DELETE on the request asynchronously.
   */
  def delete() = withMethod("DELETE").execute()

  /**
   * Perform a HEAD on the request asynchronously.
   */
  def head() = withMethod("HEAD").execute()

  /**
   * Perform a OPTIONS on the request asynchronously.
   */
  def options() = withMethod("OPTIONS").execute()

  def execute(method: String): Future[WSResponse] = withMethod(method).execute()

  /**
   * Execute this request
   */
  def execute(): Future[WSResponse]

  /**
   * Execute this request and stream the response body in an enumerator
   */
  def stream(): Future[(WSResponseHeaders, Enumerator[Array[Byte]])]
}

/**
 *
 */
trait WSAuthScheme {
  // Purposely not sealed in case clients want to add their own auth schemes.
}

object WSAuthScheme {

  case object DIGEST extends WSAuthScheme

  case object BASIC extends WSAuthScheme

  case object NTLM extends WSAuthScheme

  case object SPNEGO extends WSAuthScheme

  case object KERBEROS extends WSAuthScheme

  case object NONE extends WSAuthScheme

}

/**
 * A WS Cookie.  This is a trait so that we are not tied to a specific client.
 */
trait WSCookie {

  /**
   * The underlying "native" cookie object for the client.
   */
  def underlying[T]: T

  /**
   * The domain.
   */
  def domain: String

  /**
   * The cookie name.
   */
  def name: Option[String]

  /**
   * The cookie value.
   */
  def value: Option[String]

  /**
   * The path.
   */
  def path: String

  /**
   * The expiry date.
   */
  def expires: Option[Long]

  /**
   * The maximum age.
   */
  def maxAge: Option[Int]

  /**
   * If the cookie is secure.
   */
  def secure: Boolean
}

/**
 * A WS proxy.
 */
trait WSProxyServer {
  /** The hostname of the proxy server. */
  def host: String

  /** The port of the proxy server. */
  def port: Int

  /** The protocol of the proxy server.  Use "http" or "https".  Defaults to "http" if not specified. */
  def protocol: Option[String]

  /** The principal (aka username) of the credentials for the proxy server. */
  def principal: Option[String]

  /** The password for the credentials for the proxy server. */
  def password: Option[String]

  def ntlmDomain: Option[String]

  def encoding: Option[String]

  def nonProxyHosts: Option[Seq[String]]
}

/**
 * A WS proxy.
 */
case class DefaultWSProxyServer(
  /** The hostname of the proxy server. */
  host: String,

  /** The port of the proxy server. */
  port: Int,

  /** The protocol of the proxy server.  Use "http" or "https".  Defaults to "http" if not specified. */
  protocol: Option[String] = None,

  /** The principal (aka username) of the credentials for the proxy server. */
  principal: Option[String] = None,

  /** The password for the credentials for the proxy server. */
  password: Option[String] = None,

  ntlmDomain: Option[String] = None,

  encoding: Option[String] = None,

  nonProxyHosts: Option[Seq[String]] = None) extends WSProxyServer

/**
 * An HTTP response header (the body has not been retrieved yet)
 */
trait WSResponseHeaders {

  def status: Int

  def headers: Map[String, Seq[String]]
}

case class DefaultWSResponseHeaders(status: Int, headers: Map[String, Seq[String]]) extends WSResponseHeaders

/**
 * Sign a WS call.
 */
trait WSSignatureCalculator {

  /**
   * Sign it.
   */
  def sign(request: WSRequest)
}

Other Play Framework source code examples

Here is a short list of links related to this Play Framework WS.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.