Documentation

You are viewing the documentation for the 2.4.0-M2 development release. The latest stable release series is 3.0.x.

§The Play WS API

Sometimes we would like to call other HTTP services from within a Play application. Play supports this via its WS library, which provides a way to make asynchronous HTTP calls.

There are two important parts to using the WS API: making a request, and processing the response. We’ll discuss how to make both GET and POST HTTP requests first, and then show how to process the response from WS. Finally, we’ll discuss some common use cases.

§Making a Request

To use WS, first add javaWs to your build.sbt file:

libraryDependencies ++= Seq(
  javaWs
)

Now any controller or component that wants to use WS will have to add the following imports and then declare a dependency on the WSClient:

import javax.inject.Inject;

import play.mvc.*;
import play.libs.ws.*;
import play.libs.F.Function;
import play.libs.F.Promise;

public class Application extends Controller {

    @Inject WSClient ws;

    // ...
}

To build an HTTP request, you start with WS.url() to specify the URL.

WSRequestHolder holder = ws.url("http://example.com");

This returns a WSRequestHolder that you can use to specify various HTTP options, such as setting headers. You can chain calls together to construct complex requests.

WSRequestHolder complexHolder = holder.setHeader("headerKey", "headerValue")
                                      .setTimeout(1000)
                                      .setQueryParameter("paramKey", "paramValue");

You end by calling a method corresponding to the HTTP method you want to use. This ends the chain, and uses all the options defined on the built request in the WSRequestHolder.

Promise<WSResponse> responsePromise = complexHolder.get();

This returns a Promise<WSResponse> where the WSResponse contains the data returned from the server.

§Request with authentication

If you need to use HTTP authentication, you can specify it in the builder, using a username, password, and an WSAuthScheme. Options for the WSAuthScheme are BASIC, DIGEST, KERBEROS, NONE, NTLM, and SPNEGO.

ws.url(url).setAuth("user", "password", WSAuthScheme.BASIC).get();

§Request with follow redirects

If an HTTP call results in a 302 or a 301 redirect, you can automatically follow the redirect without having to make another call.

ws.url(url).setFollowRedirects(true).get();

§Request with query parameters

You can specify query parameters for a request.

ws.url(url).setQueryParameter("paramKey", "paramValue");

§Request with additional headers

ws.url(url).setHeader("headerKey", "headerValue").get();

For example, if you are sending plain text in a particular format, you may want to define the content type explicitly.

ws.url(url).setHeader("Content-Type", "application/json").post(jsonString);
// OR
ws.url(url).setContentType("application/json").post(jsonString);

§Request with time out

If you wish to specify a request timeout, you can use setTimeout to set a value in milliseconds.

ws.url(url).setTimeout(1000).get();

§Submitting form data

To post url-form-encoded data you can set the proper header and formatted data.

ws.url(url).setContentType("application/x-www-form-urlencoded")
           .post("key1=value1&key2=value2");

§Submitting JSON data

The easiest way to post JSON data is to use the JSON library.

import com.fasterxml.jackson.databind.JsonNode;
import play.libs.Json;
JsonNode json = Json.newObject()
                    .put("key1", "value1")
                    .put("key2", "value2");

ws.url(url).post(json);

§Processing the Response

Working with the WSResponse is done by mapping inside the Promise.

§Processing a response as JSON

You can process the response as a JsonNode by calling response.asJson().

Java
Promise<JsonNode> jsonPromise = ws.url(url).get().map(
    new Function<WSResponse, JsonNode>() {
        public JsonNode apply(WSResponse response) {
            JsonNode json = response.asJson();
            return json;
        }
    }
);
Java 8
Promise<JsonNode> jsonPromise = ws.url(url).get().map(response -> {
    return response.asJson();
});

§Processing a response as XML

Similarly, you can process the response as XML by calling response.asXml().

Java
Promise<Document> documentPromise = ws.url(url).get().map(
    new Function<WSResponse, Document>() {
        public Document apply(WSResponse response) {
            Document xml = response.asXml();
            return xml;
        }
    }
);
Java 8
Promise<Document> documentPromise = ws.url(url).get().map(response -> {
    return response.asXml();
});

§Processing large responses

When you are downloading a large file or document, WS allows you to get the response body as an InputStream so you can process the data without loading the entire content into memory at once.

Java
final Promise<File> filePromise = ws.url(url).get().map(
    new Function<WSResponse, File>() {
        public File apply(WSResponse response) throws Throwable {

            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                inputStream = response.getBodyAsStream();

                // write the inputStream to a File
                final File file = new File("/tmp/response.txt");
                outputStream = new FileOutputStream(file);

                int read = 0;
                byte[] buffer = new byte[1024];

                while ((read = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, read);
                }

                return file;
            } catch (IOException e) {
                throw e;
            } finally {
                if (inputStream != null) {inputStream.close();}
                if (outputStream != null) {outputStream.close();}
            }

        }
    }
);
Java 8
Promise<File> filePromise = ws.url(url).get().map(response -> {
    InputStream inputStream = null;
    OutputStream outputStream = null;
    try {
        inputStream = response.getBodyAsStream();
    
        // write the inputStream to a File
        final File file = new File("/tmp/response.txt");
        outputStream = new FileOutputStream(file);
    
        int read = 0;
        byte[] buffer = new byte[1024];

        while ((read = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, read);
        }
  
        return file;  
    } catch (IOException e) {
        throw e;
    } finally {
        if (inputStream != null) {inputStream.close();}
        if (outputStream != null) {outputStream.close();}
    } 
});

This example will read the response body and write it to a file in buffered increments.

§Common Patterns and Use Cases

§Chaining WS calls

You can chain WS calls by using flatMap.

Java
final Promise<WSResponse> responseThreePromise = ws.url(urlOne).get().flatMap(
    new Function<WSResponse, Promise<WSResponse>>() {
        public Promise<WSResponse> apply(WSResponse responseOne) {
            String urlTwo = responseOne.getBody();
            return ws.url(urlTwo).get().flatMap(
                new Function<WSResponse, Promise<WSResponse>>() {
                    public Promise<WSResponse> apply(WSResponse responseTwo) {
                        String urlThree = responseTwo.getBody();
                        return ws.url(urlThree).get();
                    }
                }
            );
        }
    }
);
Java 8
final Promise<WSResponse> responseThreePromise = ws.url(urlOne).get()
    .flatMap(responseOne -> ws.url(responseOne.getBody()).get())
    .flatMap(responseTwo -> ws.url(responseTwo.getBody()).get());

§Exception recovery

If you want to recover from an exception in the call, you can use recover or recoverWith to substitute a response.

Java
Promise<WSResponse> responsePromise = ws.url("http://example.com").get();
Promise<WSResponse> recoverPromise = responsePromise.recoverWith(new Function<Throwable, Promise<WSResponse>>() {
    @Override
    public Promise<WSResponse> apply(Throwable throwable) throws Throwable {
        return ws.url("http://backup.example.com").get();
    }
});
Java 8
Promise<WSResponse> responsePromise = ws.url("http://example.com").get();
Promise<WSResponse> recoverPromise = responsePromise.recoverWith(throwable ->
    ws.url("http://backup.example.com").get()
);

§Using in a controller

You can map a Promise<WSResponse> to a Promise<Result> that can be handled directly by the Play server, using the asynchronous action pattern defined in Handling Asynchronous Results.

Java
public Promise<Result> index() {
    final Promise<Result> resultPromise = ws.url(feedUrl).get().map(
            new Function<WSResponse, Result>() {
                public Result apply(WSResponse response) {
                    return ok("Feed title:" + response.asJson().findPath("title"));
                }
            }
    );
    return resultPromise;
}
Java 8
public Promise<Result> index() {
    return ws.url(feedUrl).get().map(response ->
        ok("Feed title: " + response.asJson().findPath("title").asText())
    );
}

§Using WSClient

WSClient is a wrapper around the underlying AsyncHttpClient. It is useful for defining multiple clients with different profiles, or using a mock.

The default client can be called from the WS class:

WSClient client = WS.client();

You can define a WS client directly from code and use this for making requests. Note that you must follow a particular series of steps to use HTTPS correctly if you are defining a client directly:

import com.ning.http.client.*;
import play.api.libs.ws.WSClientConfig;
import play.api.libs.ws.DefaultWSClientConfig;
import play.api.libs.ws.ssl.SSLConfig;
import play.api.libs.ws.ning.NingAsyncHttpClientConfigBuilder;
 // Set up the client config (you can also use a parser here):
 scala.Option<Object> none = scala.None$.empty();
 scala.Option<String> noneString = scala.None$.empty();
 scala.Option<SSLConfig> noneSSLConfig = scala.None$.empty();
 WSClientConfig clientConfig = new DefaultWSClientConfig(
         none, // connectionTimeout
         none, // idleTimeout
         none, // requestTimeout
         none, // followRedirects
         none, // useProxyProperties
         noneString, // userAgent
         none, // compressionEnabled
         none, // acceptAnyCertificate
         noneSSLConfig);

 // Build a secure config out of the client config:
 NingAsyncHttpClientConfigBuilder secureBuilder = new NingAsyncHttpClientConfigBuilder(clientConfig);
 AsyncHttpClientConfig secureDefaults = secureBuilder.build();

 // You can directly use the builder for specific options once you have secure TLS defaults...
AsyncHttpClientConfig customConfig = new AsyncHttpClientConfig.Builder(secureDefaults)
                 .setProxyServer(new com.ning.http.client.ProxyServer("127.0.0.1", 38080))
                 .setCompressionEnabled(true)
                 .build();
 WSClient customClient = new play.libs.ws.ning.NingWSClient(customConfig);

 Promise<WSResponse> responsePromise = customClient.url("http://example.com/feed").get();

NOTE: if you instantiate a NingWSClient object, it does not use the WS plugin system, and so will not be automatically closed in Application.onStop. Instead, the client must be manually shutdown using client.close() when processing has completed. This will release the underlying ThreadPoolExecutor used by AsyncHttpClient. Failure to close the client may result in out of memory exceptions (especially if you are reloading an application frequently in development mode).

You can also get access to the underlying AsyncHttpClient.

com.ning.http.client.AsyncHttpClient underlyingClient =
    (com.ning.http.client.AsyncHttpClient) ws.getUnderlying();

This is important in a couple of cases. WS has a couple of limitations that require access to the client:

§Configuring WS

Use the following properties in application.conf to configure the WS client:

§Timeouts

There are 3 different timeouts in WS. Reaching a timeout causes the WS request to interrupt.

The request timeout can be overridden for a specific connection with setTimeout() (see “Making a Request” section).

§Configuring WS with SSL

To configure WS for use with HTTP over SSL/TLS (HTTPS), please see Configuring WS SSL.

Next: Connecting to OpenID services