Play framework 1.0 content negotiation
One thing that the Play framework 1.0 has in common with other RESTful architectures is the direct use of HTTP functionality, instead of trying to hide HTTP or put an abstraction layer on top of it. This article shows you how to use HTTP content negotiation in a Play framework web application.
Content negotiation
Content negotiation is
an HTTP feature that allows an HTTP server to serve different
media types for the
same URL, according to which media types are requested by the HTTP
client. The client specifies acceptable content types using media types
in the Accept
header, such as requiring an XML response with:
Accept: application/xml
A client may specify more than one media type, and also specify that any
media type is acceptable with a catch-all wild-card media type (/
):
Accept: application/xml, image/png, */*
Conventional web browsers always include the wild-card value in the
Accept
header: they will accept any media type, and Play will serve
HTML - the default 'format'. Content negotiation is more likely to be
used by custom clients, such as an Ajax request that requires a JSON
response, or an e-book reader that requires a PDF or EPUB version of a
document.
Play request format
Play implements content negotiation by parsing the Accept
header and
setting request.format
, which is used to select the view template by
file extension.
The default format for a Play request is html
, which is selected if
the Accept
header contains text/html
or application/xhtml
, or as a
result of the wildcard /
value. The default format is not selected
if the wildcard value is not present.
The default template for the index()
controller method (and html
format) is therefore the file index.html
. If you specify a different
format, in one of several ways, you can select an alternate template.
Built-in formats
Play has built-in support for a few formats: html
, txt
, json
and
xml
. For example, define a controller method that renders some data:
public static void index() {
final String name = "Peter Hilton";
final String organisation = "Lunatech Research";
final String url = "http://www.lunatech-research.com/";
render(name, organisation, url);
}
If you request a URL that is mapped to this method
(http://localhost:9000/
in a new Play application) in a web browser,
then play will render the index.html
template, because web browsers
send an Accept
header that includes the value text/html
.
Play responds to a request with the header Accept: text/xml
by setting
the request format to xml
and rendering an index.xml
template, such
as:
<?xml version="1.0"?>
<contact>
<name>${name}</name>
<organisation>${organisation}</organisation>
<url>${url}</url>
</contact>
The built in Accept
header format mappings work as follows, for an
index()
controller method.
Accept header |
Format | Template file name | Mapping |
---|---|---|---|
|
|
|
Default template extension for |
|
|
|
Media type not mapped to a format |
|
|
|
Default media type mapped to
|
|
|
|
Built-in format |
|
|
|
Built-in format |
|
|
|
Built-in format |
|
|
|
Built-in format |
|
|
|
Built-in format |
|
|
|
Built-in format |
|
|
|
Built-in format, default media type ignored |
Custom formats
If you want to serve a response with a particular media type you can set
the format before calling the render
method. For example, to serve a
vCard, you can do:
request.format = "vcf";
Instead of applying this format to all requests, you can implement content negotiation by inspecting the request headers, so that you only set that format when the HTTP request selects the corresponding media type. In your controller, check for your custom format before all requests:
@Before
static void setFormat() {
if (request.headers.get("accept").value().equals("text/x-vcard")) {
request.format = "vcf";
}
}
Now, a request with a Accept: text/x-vcard
header will render an
index.vcf
template, such as:
BEGIN:VCARD
VERSION:3.0
N:${name}
FN:${name}
ORG:${organisation}
URL:${url}
END:VCARD
Note that this also sets the response Content-type
to text/x-vcard
because of the media type mapping in Play’s mime-types.properties
file, which may allow your system to open the vCard in a suitable
application, such as Address Book on a Mac.
URL-based format specification
An alternative to using HTTP headers is to use the URL to specify the format. This is less in the spirit of HTTP, but can be useful as a fallback for supporting HTTP clients that cannot control the headers they send.
You can add formats as specific routes, by specifying the format for the
controller method. With the same index()
method as above, the
following route will handle a request for /index.xml
, setting the
format to xml
and rendering the index.xml
template.
GET /index.xml Application.index(format:'xml')
Play can also extract the format directly from the URL, with a route such as the following.
GET /index.{format} Application.index
With this route, a request for /index.xml
will set the format to xml
and render the XML template, while /index.txt
will render the plain
text template.