How can my Play 2 app respond to different “Accept

2020-05-27 04:45发布

问题:

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?

回答1:

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)



回答2:

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