• Stars
    star
    220
  • Rank 174,041 (Top 4 %)
  • Language
    Scala
  • License
    Apache License 2.0
  • Created over 7 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Standalone Play WS, an async HTTP client with fluent API

Play WS Standalone

Twitter Follow Discord GitHub Discussions StackOverflow YouTube Twitch Status OpenCollective

Build Status Maven Javadocs Repository size Scala Steward badge Mergify Status

Play WS is a powerful HTTP Client library, originally developed by the Play team for use with Play Framework. It uses AsyncHttpClient for HTTP client functionality and has no Play dependencies.

We've provided some documentation here on how to use Play WS in your app (without Play). For more information on how to use Play WS in Play, please refer to the Play documentation.

Getting Started

To get started, you can add play-ahc-ws-standalone as a dependency in SBT:

libraryDependencies += "com.typesafe.play" %% "play-ahc-ws-standalone" % "LATEST_VERSION"

Where you replace LATEST_VERSION with the version shown in this image: Latest released version.

This adds the standalone version of Play WS, backed by AsyncHttpClient. This library contains both the Scala and Java APIs, under play.api.libs.ws and play.libs.ws.

To add XML and JSON support using Play-JSON or Scala XML, add the following:

libraryDependencies += "com.typesafe.play" %% "play-ws-standalone-xml" % playWsStandaloneVersion
libraryDependencies += "com.typesafe.play" %% "play-ws-standalone-json" % playWsStandaloneVersion

Shading

Play WS uses shaded versions of AsyncHttpClient and OAuth Signpost, repackaged under the play.shaded.ahc and play.shaded.oauth package names, respectively. Shading AsyncHttpClient means that the version of Netty used behind AsyncHttpClient is completely independent of the application and Play as a whole.

Specifically, shading AsyncHttpClient means that there are no version conflicts introduced between Netty 4.0 and Netty 4.1 using Play WS.

NOTE: If you are developing play-ws and publishing shaded-asynchttpclient and shaded-oauth using sbt publishLocal, you need to be aware that updating ~/.ivy2/local does not overwrite ~/.ivy2/cache and so you will not see your updated shaded code until you remove it from cache. See http://eed3si9n.com/field-test for more details. This bug has been filed as sbt/sbt#2687.

Shaded AHC Defaults

Because Play WS shades AsyncHttpClient, the default settings are also shaded and so do not adhere to the AHC documentation. This means that the settings in ahc-default.properties and the AsyncHttpClient system properties are prepended with play.shaded.ahc, for example the usePooledMemory setting in the shaded version of AsyncHttpClient is defined like this:

play.shaded.ahc.org.asynchttpclient.usePooledMemory=true

Typed Bodies

The type system in Play-WS has changed so that the request body and the response body can use richer types.

You can define your own BodyWritable or BodyReadable, but if you want to use the default out of the box settings, you can import the type mappings with the DefaultBodyReadables / DefaultBodyWritables.

Scala

import play.api.libs.ws.DefaultBodyReadables._
import play.api.libs.ws.DefaultBodyWritables._

More likely you will want the XML and JSON support:

import play.api.libs.ws.XMLBodyReadables._
import play.api.libs.ws.XMLBodyWritables._

or

import play.api.libs.ws.JsonBodyReadables._
import play.api.libs.ws.JsonBodyWritables._

To use a BodyReadable in a response, you must type the response explicitly:

import scala.concurrent.{ ExecutionContext, Future }

import play.api.libs.ws.StandaloneWSClient
import play.api.libs.ws.XMLBodyReadables._ // required

def handleXml(ws: StandaloneWSClient)(
  implicit ec: ExecutionContext): Future[scala.xml.Elem] =
  ws.url("...").get().map { response =>
    response.body[scala.xml.Elem]
  }

or using Play-JSON:

import scala.concurrent.{ ExecutionContext, Future }

import play.api.libs.json.JsValue
import play.api.libs.ws.StandaloneWSClient

import play.api.libs.ws.JsonBodyReadables._ // required

def handleJsonResp(ws: StandaloneWSClient)(
  implicit ec: ExecutionContext): Future[JsValue] =
  ws.url("...").get().map { response =>
    response.body[JsValue]
  }

Note that there is a special case: when you are streaming the response, then you should get the body as a Source:

import scala.concurrent.ExecutionContext
import akka.util.ByteString
import akka.stream.scaladsl.Source
import play.api.libs.ws.StandaloneWSClient

def useWSStream(ws: StandaloneWSClient)(implicit ec: ExecutionContext) =
  ws.url("...").stream().map { response =>
     val source: Source[ByteString, _] = response.bodyAsSource
     val _ = source // do something with source
  }

To POST, you should pass in a type which has an implicit class mapping of BodyWritable:

import scala.concurrent.ExecutionContext
import play.api.libs.ws.DefaultBodyWritables._ // required

def postExampleString(ws: play.api.libs.ws.StandaloneWSClient)(
  implicit ec: ExecutionContext) = {
  val stringData = "Hello world"
  ws.url("...").post(stringData).map { response => /* do something */ }
}

You can also define your own custom BodyReadable:

import play.api.libs.ws.BodyReadable
import play.api.libs.ws.ahc.StandaloneAhcWSResponse

case class Foo(body: String)

implicit val fooBodyReadable = BodyReadable[Foo] { response =>
  import play.shaded.ahc.org.asynchttpclient.{ Response => AHCResponse }
  val ahcResponse = response.asInstanceOf[StandaloneAhcWSResponse].underlying[AHCResponse]
  Foo(ahcResponse.getResponseBody)
}

or custom BodyWritable:

import akka.util.ByteString
import play.api.libs.ws.{ BodyWritable, InMemoryBody }

implicit val writeableOf_Foo: BodyWritable[Foo] = {
  // https://tools.ietf.org/html/rfc6838#section-3.2
  BodyWritable(foo => InMemoryBody(ByteString.fromString(foo.toString)), "application/vnd.company.category+foo")
}

Java

To use the default type mappings in Java, you should use the following:

import play.libs.ws.DefaultBodyReadables;
import play.libs.ws.DefaultBodyWritables;

followed by:

public class MyClient implements DefaultBodyWritables, DefaultBodyReadables {    
    public CompletionStage<String> doStuff() {
      return client.url("http://example.com").post(body("hello world")).thenApply(response ->
        response.body(string())
      );
    }
}

Note that there is a special case: when you are using a stream, then you should get the body as a Source:

class MyClass {
    public CompletionStage<Source<ByteString, NotUsed>> readResponseAsStream() {
        return ws.url(url).stream().thenApply(response ->
            response.bodyAsSource()
        );
    }
}

You can also post a Source:

class MyClass {
    public CompletionStage<String> doStuff() {
        Source<ByteString, NotUsed> source = fromSource();
        return ws.url(url).post(body(source)).thenApply(response ->
            response.body()
        );
    }
}

You can define a custom BodyReadable:

import play.libs.ws.ahc.*;
import play.shaded.ahc.org.asynchttpclient.Response;

class FooReadable implements BodyReadable<StandaloneWSResponse, Foo> {
    public Foo apply(StandaloneWSResponse response) {
        Response ahcResponse = (Response) response.getUnderlying();
        return Foo.serialize(ahcResponse.getResponseBody(StandardCharsets.UTF_8));
    }
}

You can also define your own custom BodyWritable:

public class MyClient {
    private BodyWritable<String> someOtherMethod(String string) {
      akka.util.ByteString byteString = akka.util.ByteString.fromString(string);
      return new DefaultBodyWritables.InMemoryBodyWritable(byteString, "text/plain");
    }
}

Instantiating a standalone client

The standalone client needs Akka to handle streaming data internally:

Scala

In Scala, the way to call out to a web service and close down the client:

package playwsclient

import akka.actor.ActorSystem
import akka.stream._
import play.api.libs.ws._
import play.api.libs.ws.ahc._

import scala.concurrent.Future

object ScalaClient {
  import DefaultBodyReadables._
  import scala.concurrent.ExecutionContext.Implicits._

  def main(args: Array[String]): Unit = {
    // Create Akka system for thread and streaming management
    implicit val system = ActorSystem()
    system.registerOnTermination {
      System.exit(0)
    }

    implicit val materializer = SystemMaterializer(system).materializer

    // Create the standalone WS client
    // no argument defaults to a AhcWSClientConfig created from
    // "AhcWSClientConfigFactory.forConfig(ConfigFactory.load, this.getClass.getClassLoader)"
    val wsClient = StandaloneAhcWSClient()

    call(wsClient)
      .andThen { case _ => wsClient.close() }
      .andThen { case _ => system.terminate() }
  }

  def call(wsClient: StandaloneWSClient): Future[Unit] = {
    wsClient.url("http://www.google.com").get().map { response =>
      val statusText: String = response.statusText
      val body = response.body[String]
      println(s"Got a response $statusText: $body")
    }
  }
}

You can also create the standalone client directly from an AsyncHttpClient instance:

object ScalaClient {
  def main(args: Array[String]): Unit = {
    // Use 
    import play.shaded.ahc.org.asynchttpclient._
    val asyncHttpClientConfig = new DefaultAsyncHttpClientConfig.Builder()
      .setMaxRequestRetry(0)
      .setShutdownQuietPeriod(0)
      .setShutdownTimeout(0).build
    val asyncHttpClient = new DefaultAsyncHttpClient(asyncHttpClientConfig)
    val wsClient = new StandaloneAhcWSClient(asyncHttpClient)
    /// ...
  }
}

This is useful when there is an AsyncHttpClient configuration option that is not available in the WS config layer.

Java

In Java the API is much the same:

package playwsclient;

import akka.actor.ActorSystem;
import akka.stream.*;
import com.typesafe.config.ConfigFactory;

import play.libs.ws.*;
import play.libs.ws.ahc.*;

public class JavaClient implements DefaultBodyReadables {
    private final StandaloneAhcWSClient client;
    private final ActorSystem system;

    public static void main(String[] args) {
        // Set up Akka materializer to handle streaming
        final String name = "wsclient";
        ActorSystem system = ActorSystem.create(name);
        system.registerOnTermination(() -> System.exit(0));
        Materializer materializer = SystemMaterializer.get(system).materializer();

        // Create the WS client from the `application.conf` file, the current classloader and materializer.
        StandaloneAhcWSClient ws = StandaloneAhcWSClient.create(
                AhcWSClientConfigFactory.forConfig(ConfigFactory.load(), system.getClass().getClassLoader()),
                materializer
        );

        JavaClient javaClient = new JavaClient(system, ws);
        javaClient.run();
    }

    JavaClient(ActorSystem system, StandaloneAhcWSClient client) {
        this.system = system;
        this.client = client;
    }

    public void run() {
        client.url("http://www.google.com").get()
                .whenComplete((response, throwable) -> {
                    String statusText = response.getStatusText();
                    String body = response.getBody(string());
                    System.out.println("Got a response " + statusText);
                })
                .thenRun(() -> {
                    try {
                        client.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                })
                .thenRun(system::terminate);
    }
}

Likewise, you can provide the AsyncHttpClient client explicitly from configuration:

public class JavaClient implements DefaultBodyReadables {
     public static void main(String[] args) { 
        // ...
        // Set up AsyncHttpClient directly from config
        AsyncHttpClientConfig asyncHttpClientConfig =
            new DefaultAsyncHttpClientConfig.Builder()
                .setMaxRequestRetry(0)
                .setShutdownQuietPeriod(0)
                .setShutdownTimeout(0)
                .build();
        AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient(asyncHttpClientConfig);
    
        // Set up WSClient instance directly from asynchttpclient.
        WSClient client = new AhcWSClient(asyncHttpClient, materializer);
        // ...
    }
}

Caching

Play WS implements HTTP Caching through CachingAsyncHttpClient, AhcHTTPCache and CacheControl, a minimal HTTP cache management library in Scala.

To create a standalone AHC client that uses caching, pass in an instance of AhcHttpCache with a cache adapter to the underlying implementation. For example, to use Caffeine as the underlying cache, you could use the following:

import scala.concurrent.Future
import java.util.concurrent.TimeUnit
import com.github.benmanes.caffeine.cache.{ Caffeine, Ticker }

import play.api.libs.ws.ahc.StandaloneAhcWSClient
import play.api.libs.ws.ahc.cache.{
  AhcHttpCache, Cache, EffectiveURIKey, ResponseEntry
}

class CaffeineHttpCache extends Cache {
  val underlying = Caffeine.newBuilder()
    .ticker(Ticker.systemTicker())
    .expireAfterWrite(365, TimeUnit.DAYS)
    .build[EffectiveURIKey, ResponseEntry]()

  def remove(key: EffectiveURIKey) =
    Future.successful(Option(underlying.invalidate(key)))

  def put(key: EffectiveURIKey, entry: ResponseEntry) =
    Future.successful(underlying.put(key, entry))

  def get(key: EffectiveURIKey) =
    Future.successful(Option(underlying getIfPresent key ))

  def close(): Unit = underlying.cleanUp()
}

def withCache(implicit m: akka.stream.Materializer): StandaloneAhcWSClient = {
  implicit def ec = m.executionContext

  val cache = new CaffeineHttpCache()
  StandaloneAhcWSClient(httpCache = Some(new AhcHttpCache(cache)))
}

There are a number of guides that help with putting together Cache-Control headers:

Releasing a new version

See https://github.com/playframework/.github/blob/main/RELEASING.md

License

Play WS is licensed under the Apache license, version 2. See the LICENSE file for more information.

More Repositories

1

playframework

The Community Maintained High Velocity Web Framework For Java and Scala.
Scala
12,479
star
2

play1

Play framework
Java
1,578
star
3

play-slick

Slick Plugin for Play
Scala
799
star
4

twirl

Twirl is Play's default template engine
Scala
535
star
5

play-samples

Play Framework Sample Projects
JavaScript
501
star
6

play-plugins

CachePlugin
JavaScript
444
star
7

play-json

The Play JSON library
Scala
354
star
8

play-mailer

Play mailer plugin
Scala
249
star
9

play-scala-starter-example

Play Scala Starter Template (ideal for new users!)
CSS
235
star
10

anorm

The Anorm database library
Scala
233
star
11

play-scala-rest-api-example

Example Play Scala application showing REST API
Scala
212
star
12

play-scala-react-seed

❄️ Scala Play + React seed project with full-fledged build process
Shell
200
star
13

play-scala-websocket-example

Example Play Scala application showing WebSocket use with Akka actors
Scala
194
star
14

play-java-starter-example

Play starter project in Java (ideal for new users!)
CSS
160
star
15

play-scala-isolated-slick-example

Example Play Slick Project
Scala
154
star
16

netty-reactive-streams

Reactive streams implementation for Netty.
Java
111
star
17

scalatestplus-play

ScalaTest + Play
Scala
104
star
18

play-ebean

Play Ebean module
Java
100
star
19

play-socket.io

Play socket.io support
Scala
92
star
20

play-java-websocket-example

Example Play Java application showing Websocket usage with Akka actors
Java
88
star
21

play-scala-angular-seed

🍀 Scala Play 2.7.x + Angular 8 with Angular CLI seed project with full-fledged build process
TypeScript
85
star
22

prune

Performance testing tool for Play Framework
Scala
80
star
23

play-scala-seed.g8

Play Scala Seed Template: run "sbt new playframework/play-scala-seed.g8"
Scala
70
star
24

play-silhouette

Silhouette is an authentication library for Play Framework applications that supports several authentication methods, including OAuth1, OAuth2, OpenID, CAS, 2FA, TOTP, Credentials, Basic Authentication or custom authentication schemes.
Scala
68
star
25

play-scala-slick-example

Example Play Scala project with Slick
Scala
59
star
26

modules.playframework.org

Java
56
star
27

play-scala-secure-session-example

An example Play application showing encrypted session management
Scala
56
star
28

play-java-ebean-example

Example Play application showing Java with Ebean
Java
53
star
29

play-java-angular-seed

🍁 Java Play 2.7.x + Angular 8 with Angular CLI seed project with full-fledged build process
TypeScript
52
star
30

play-scala-macwire-di-example

Sample project for compile-time DI with Macwire
Scala
47
star
31

playframework.com

The Play Framework website
Scala
44
star
32

play-java-rest-api-example

REST API using Play in Java
Java
44
star
33

play-scala-chatroom-example

Play chatroom with Scala API
Scala
43
star
34

play-scala-tls-example

A Play application using HTTPS and WS with optional client authentication
Scala
43
star
35

play-java-react-seed

🌀 Java Play 2.7.x + React seed project with full-fledged build process
Shell
43
star
36

play-scala-streaming-example

Example Play application showing Comet and Server Sent Events in Scala
Scala
42
star
37

play-scala-anorm-example

Example Play Database Application using Anorm
Scala
40
star
38

play-java-dagger2-example

Play Application using Dagger 2 for Compile Time DI
Java
38
star
39

play-scala-compile-di-example

Example Play Project using compile time dependency injection and Play WS with ScalaTest
Scala
37
star
40

play-soap

Play SOAP support
Scala
34
star
41

play-grpc

Play + Akka gRPC
Scala
34
star
42

play-java-jpa-example

Example Play Java project with JPA database integration
Java
34
star
43

play-java-chatroom-example

Example Chatroom with Java API
Java
33
star
44

play-scala-fileupload-example

An example Play application showing custom multiform fileupload in Scala
Scala
28
star
45

play-java-seed.g8

Play Java Seed template: use "sbt new playframework/play-java-seed.g8"
Java
21
star
46

play-doc

Play documentation rendering support
Scala
20
star
47

play-webgoat

A vulnerable Play application for attackers.
Scala
17
star
48

play-iteratees

Scala
17
star
49

play-scala-forms-example

Example Play Project showing form processing
Scala
15
star
50

play-glassfish

Play container for Glassfish
Java
15
star
51

cachecontrol

Minimal HTTP cache management library in Scala
Scala
13
star
52

play-scala-log4j2-example

An example Play project using Log4J 2 as the logging engine
Scala
13
star
53

play-enhancer

Java
13
star
54

play-java-fileupload-example

An example Play application showing custom multiform fileupload in Java
Java
13
star
55

play-file-watch

This is the Play File Watch library
Java
12
star
56

interplay

Common sbt plugins for Play modules
Scala
11
star
57

play-generated-docs

Generated docs for publishing to the Play website.
10
star
58

play-scala-hello-world-tutorial

Hello World Tutorial for Play in Scala
HTML
9
star
59

play-spring-loader

An application loader for Play that uses Spring as the dependency injection framework
Scala
9
star
60

play-java-streaming-example

Example Play application showing Comet and Server Sent Events in Java
JavaScript
8
star
61

play-scala-grpc-example

Example for akka-grpc services embedded in Play framework applications (Scala)
CSS
8
star
62

play-meta

Team management & cross-repository effort tracking
Shell
7
star
63

play-java-hello-world-tutorial

Play Hello World tutorial in Java
HTML
5
star
64

omnidoc

Play aggregated documentation
Scala
5
star
65

.github

Scala
4
star
66

play-java-forms-example

Play Project Template showing Java Forms Processing
Java
4
star
67

play-java-compile-di-example

Example Play application using compile time DI with Java API
Java
3
star
68

play-java-grpc-example

Example for akka-grpc services embedded in Play framework applications (Java)
CSS
3
star
69

templatecontrol

Template Control for Play templates and other examples
Scala
3
star
70

play-quota-scala-example

An example of User Quotas using Play and Scala
Scala
2
star
71

modules.playframework.com

The Play Framework module index
Scala
2
star
72

play-native-loader

Loading native libraries in Play Framework
Java
2
star
73

play-quota-java-example

An example application using Play Quota with Java API
Java
2
star
74

playframework.github.io

1
star
75

jnotify

Scala
1
star