Skip to content

Commit

Permalink
Content negotiation documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
julienrf committed Nov 17, 2012
1 parent 213594d commit 7353096
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 10 deletions.
18 changes: 18 additions & 0 deletions documentation/manual/Highlights.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,21 @@ requireJs := "main.js"
```

More information about this feature can be found on the [[RequireJS documentation page|RequireJS-support]].

## Content negotiation

The support of content negotiation has been improved, for example now controllers automatically choose the most appropriate lang according to the quality values set in the `Accept-Language` request header value.

It is also easier to write Web Services supporting several representations of a same resource and automatically choosing the best according to the `Accept` request header value:

```
val list = Action { implicit request =>
val items = Item.findAll
render {
case Accepts.Html() => Ok(views.html.list(items))
case Accepts.Json() => Ok(Json.toJson(items))
}
}
```

More information can be found on the content negotiation documentation pages for [[Scala|ScalaContentNegotiation]] and [[Java|JavaContentNegotiation]].
2 changes: 2 additions & 0 deletions documentation/manual/book/Book
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ScalaResults
ScalaSessionFlash
ScalaBodyParsers
ScalaActionsComposition
ScalaContentNegotiation
ScalaAsync
ScalaStream
ScalaComet
Expand Down Expand Up @@ -50,6 +51,7 @@ JavaResponse
JavaSessionFlash
JavaBodyParsers
JavaActionsComposition
JavaContentNegotiation
JavaAsync
JavaStream
JavaComet
Expand Down
1 change: 1 addition & 0 deletions documentation/manual/javaGuide/JavaHome.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The Java API for the Play application developers is available in the `play` pack
1. [[Session and Flash scopes | JavaSessionFlash]]
1. [[Body parsers | JavaBodyParsers]]
1. [[Action composition | JavaActionsComposition]]
1. [[Content negotiation | JavaContentNegotiation]]
1. [[Asynchronous HTTP programming | JavaAsync]]
1. [[Handling asynchronous results | JavaAsync]]
1. [[Streaming HTTP responses | JavaStream]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@ public Admin extends Controller {
}
```

> **Next:** [[Asynchronous HTTP programming | JavaAsync]]
> **Next:** [[Content negotiation | JavaContentNegotiation]]
28 changes: 28 additions & 0 deletions documentation/manual/javaGuide/main/http/JavaContentNegotiation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Content negotiation

Content negotiation is a mechanism that makes it possible to serve different representation of a same resource (URI). It is useful *e.g.* for writing Web Services supporting several output formats (XML, JSON, etc.). Server-driven negotiation is essentially performed using the `Accept*` requests headers. You can find more information on content negotiation in the [[HTTP specification|http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html]].

## Language

You can get the list of acceptable languages for a request using the `play.mvc.Http.RequestHeader#acceptLanguages` method that retrieves them from the `Accept-Language` header and sorts them according to their quality value. Play uses it to set the `lang` value of request’s HTTP context, so they automatically use the best possible language (if supported by your application, otherwise your application’s default language is used).

## Content

Similarly, the `play.mvc.Http.RequestHeader#acceptedTypes` method gives the list of acceptable result’s MIME types for a request. It retrieves them from the `Accept` request header and sorts them according to their quality factor.

You can test if a given MIME type is acceptable for the current request using the `play.mvc.Http.RequestHeader#accepts` method:

```
public static Result list() {
List<Item> items = Item.find.all();
if (request().accepts("text/html")) {
return ok(views.html.Application.list.render(items));
} else {
ObjectNode result = Json.newObject();
...
return ok(result);
}
}
```

> **Next:** [[Asynchronous HTTP programming | JavaAsync]]
1 change: 1 addition & 0 deletions documentation/manual/javaGuide/main/http/_Sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [[Session and Flash scopes | JavaSessionFlash]]
- [[Body parsers | JavaBodyParsers]]
- [[Actions composition | JavaActionsComposition]]
- [[Content negotiation | JavaContentNegotiation]]

### Main concepts

Expand Down
1 change: 1 addition & 0 deletions documentation/manual/scalaGuide/ScalaHome.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The Scala API for Play application developers is available in the `play.api` pac
1. [[Session and Flash scopes | ScalaSessionFlash]]
1. [[Body parsers | ScalaBodyParsers]]
1. [[Actions composition | ScalaActionsComposition]]
1. [[Content negotiation | ScalaContentNegotiation]]
1. [[Asynchronous HTTP programming | ScalaAsync]]
1. [[Handling asynchronous results | ScalaAsync]]
1. [[Streaming HTTP responses | ScalaStream]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,4 @@ def Authenticated(f: AuthenticatedRequest[AnyContent] => Result): Action[AnyCont
}
```

> **Next:** [[Asynchronous HTTP programming | ScalaAsync]]
> **Next:** [[Content negotiation | ScalaContentNegotiation]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Content negotiation

Content negotiation is a mechanism that makes it possible to serve different representation of a same resource (URI). It is useful *e.g.* for writing Web Services supporting several output formats (XML, JSON, etc.). Server-driven negotiation is essentially performed using the `Accept*` requests headers. You can find more information on content negotiation in the [[HTTP specification|http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html]].

# Language

You can get the list of acceptable languages for a request using the `play.api.mvc.RequestHeader#acceptLanguages` method that retrieves them from the `Accept-Language` header and sorts them according to their quality value. Play uses it in the `play.api.mvc.Controller#lang` method that provides an implicit `play.api.i18n.Lang` value to your actions, so they automatically use the best possible language (if supported by your application, otherwise your application’s default language is used).

# Content

Similarly, the `play.api.mvc.RequestHeader#acceptedTypes` method gives the list of acceptable result’s MIME types for a request. It retrieves them from the `Accept` request header and sorts them according to their quality factor.

Actually, the `Accept` header does not really contain MIME types but media ranges (*e.g.* a request accepting all text results may set the `text/*` range, and the `*/*` range means that all result types are acceptable). Controllers provide a higher-level `render` method to help you to handle media ranges. Consider for example the following action definition:

```
val list = Action { implicit request =>
val items = Item.findAll
render {
case Accepts.Html() => Ok(views.html.list(items))
case Accepts.Json() => Ok(Json.toJson(items))
}
}
```

`Accepts.Html()` and `Accepts.Json()` are extractors testing if a given media range matches `text/html` and `application/json`, respectively. The `render` method takes a partial function from `play.api.http.MediaRange` to `play.api.mvc.Result` and tries to apply it to each media range found in the request `Accept` header, in order of preference. If none of the acceptable media ranges is supported by your function, the `NotAcceptable` result is returned.

For example, if a client makes a request with the following value for the `Accept` header: `*/*;q=0.5,application/json`, meaning that it accepts any result type but prefers JSON, the above code will return the JSON representation. If another client makes a request with the following value for the `Accept` header: `application/xml`, meaning that it only accepts XML, the above code will return `NotAcceptable`.

# Request extractors

See the API documentation of the `play.api.mvc.AcceptExtractors.Accepts` object for the list of the MIME types supported by Play out of the box in the `render` method. You can easily create your own extractor for a given MIME type using the `play.api.mvc.Accepting` case class, for example the following code creates an extractor checking that a media range matches the `audio/mp3` MIME type:

```
val AcceptsMp3 = Accepting("audio/mp3")
...
render {
case AcceptsMp3() => ...
}
```

> **Next:** [[Asynchronous HTTP programming | ScalaAsync]]
1 change: 1 addition & 0 deletions documentation/manual/scalaGuide/main/http/_Sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [[Session and Flash scopes | ScalaSessionFlash]]
- [[Body parsers | ScalaBodyParsers]]
- [[Actions composition | ScalaActionsComposition]]
- [[Content negotiation | ScalaContentNegotiation]]

### Main concepts

Expand Down
8 changes: 4 additions & 4 deletions framework/src/play/src/main/java/play/mvc/Http.java
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ public abstract static class RequestHeader {
public abstract String path();

/**
* The Request Langs, extracted from the Accept-Language header.
* The Request Langs extracted from the Accept-Language header and sorted by preference (preferred first).
*/
public abstract List<play.i18n.Lang> acceptLanguages();

Expand All @@ -246,15 +246,15 @@ public abstract static class RequestHeader {
public abstract List<String> accept();

/**
* @return The media types set in the request Accept header, sorted in preference order.
* @return The media types set in the request Accept header, sorted by preference (preferred first).
*/
public abstract List<play.api.http.MediaRange> acceptedTypes();

/**
* Check if this request accepts a given media type.
* @return true if <code>mediaType</code> is in the Accept header, otherwise false
* @return true if <code>mimeType</code> is in the Accept header, otherwise false
*/
public abstract boolean accepts(String mediaType);
public abstract boolean accepts(String mimeType);

/**
* The query string content.
Expand Down
8 changes: 4 additions & 4 deletions framework/src/play/src/main/scala/play/api/mvc/Http.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ package play.api.mvc {
lazy val domain: String = host.split(':').head

/**
* The Request Langs, extracted from the Accept-Language header.
* The Request Langs extracted from the Accept-Language header and sorted by preference (preferred first).
*/
lazy val acceptLanguages: Seq[play.api.i18n.Lang] = {
val langs = acceptHeader(HeaderNames.ACCEPT_LANGUAGE).map(item => (item._1, Lang.get(item._2)))
Expand All @@ -101,7 +101,7 @@ package play.api.mvc {
}

/**
* @return The media types list of the request’s Accept header, sorted by preference.
* @return The media types list of the request’s Accept header, sorted by preference (preferred first).
*/
lazy val acceptedTypes: Seq[play.api.http.MediaRange] = {
val mediaTypes = acceptHeader(HeaderNames.ACCEPT).map(item => (item._1, MediaRange(item._2)))
Expand All @@ -126,9 +126,9 @@ package play.api.mvc {

/**
* Check if this request accepts a given media type.
* @return true if `mediaType` matches the Accept header, otherwise false
* @return true if `mimeType` matches the Accept header, otherwise false
*/
def accepts(mediaType: String): Boolean = {
def accepts(mimeType: String): Boolean = {
acceptedTypes.isEmpty || acceptedTypes.find(_.accepts(mediaType)).isDefined
}

Expand Down

0 comments on commit 7353096

Please sign in to comment.