10

In Rails, I was able to do something similar to the following:

respond_to do |format|
  format.xml { ... }
  format.json { ... }
end

and the appropriate block would be executed based on what the client supplied in the Accept header.

How can I do the same thing in Play 2.0 (Scala)?

I'd look to do something that looks roughly like this:

try {
  Resources.delete(id)
  Ok("done")
} 
catch { 
  case e: ClientReportableException =>
    ?? match { 
      case "application/xml" => Ok(<error>{e.message}</error>)
      case "application/json" => Ok(...)
  }
}

Is there a Play idiom for this, or do I just fetch the value of the Accept header from the request?

Bill
  • 44,502
  • 24
  • 122
  • 213

2 Answers2

14

In Play 2.1 you can write the following:

request match {
  case Accepts.Xml() => Ok(<error>{e.message}</error>)
  case Accepts.Json() => Ok(…)
}

The cases statements are tried in the order they are written, so if your client sets the HTTP Accept header to */* the first one will match (in this example case Accepts.Xml()). So, you usually want to write the Accepts.Html() case first because browsers set the Accept header to */*.

(Note: you may also be interested in this answer for a similar question in Java)

Community
  • 1
  • 1
Julien Richard-Foy
  • 9,634
  • 2
  • 36
  • 42
  • 1
    Action { case Accepts.Xml() => Ok({e.message}); case Accepts.Json() => Ok(…) } – Sadache Jun 16 '12 at 10:59
  • Thanks, this looks like what I need - but isn't Play 2.1 still under development? – Bill Jun 16 '12 at 11:24
  • This feature is not available in previous Play 2.0 releases so you have to wait for the 2.1 release or you can build Play by yourself – Julien Richard-Foy Jun 16 '12 at 13:27
  • I'm on Heroku, so building my own is nontrivial. How far away is 2.1 from general release? – Bill Jun 16 '12 at 16:09
  • 1
    If you need them now, and you can accept to remove the workaround later (when switching to 2.1), you can simply take that feature from the branch. Actually, they're just extractors... looking in the headers, blahblhablha ^^ – Andy Petrella Jun 17 '12 at 21:04
  • Well, I thought I could use `request.accepts` to get the job done for now, but even though that method exists in the Http trait and the Request class inherits from Http, that code doesn't seem to compile for me. Is there something I'm doing wrong? – Bill Jun 18 '12 at 14:54
  • Request headers from js libraries just like angular or jQuery, send accept headers like this: application/json, text/javascript, */*; q=0.01. Which will end up with a server response of html as of your example. How do you handle this? Can I match over the preferred way? i.e. the first Accepts header? – Roger Sep 09 '14 at 20:34
1

I have just released a Play! 2.0 module for content negotiation called mimerender (http://github.com/martinblech/play-mimerender).

The idea is that you have to define a mapping from your domain classes to different representations:

val m = mapping(
  "text/html" -> { s: String => views.html.index(s) },
  "application/xml" -> { s: String => <message>{s}</message> },
  "application/json" -> { s: String => toJson(Map("message" -> toJson(s))) },
  "text/plain" -> identity[String]_
)

Once you have done that once, you can reuse that mapping throughout all your controllers:

object Application extends Controller {
  def index = Action { implicit request =>
    m.status(200)("Hello, world!")
  }
}

Please note it's a very early release and has only been tested on Play 2.0.4

Martin Blech
  • 13,135
  • 6
  • 31
  • 35