Request Content-Type in Play! Framework for REST w

2020-02-12 08:29发布

问题:

I'm trying to implement a REST webservice with the Play! framework. I know how I can return a response in different formats (JSON, XML, HTML, ...) by specifying multiple templates. However, I didn't find any information on how you should process different Content-Types in a (e.g. POST) request (form encoded, JSON, XML, ...).

Is it possible to annotate a method to match only certain Content-Types (something like @Consumes)? Do I have to differentiate between the different request Content-Types with an if-clause in the controller method?

回答1:

Have a look at the Play documentation for combining body parsers:

http://www.playframework.com/documentation/2.2.0/ScalaBodyParsers

If you want to constrain a post body to only xml or json you can write something along these lines:

val xmlOrJson = parse.using {
  request =>
    request.contentType.map(_.toLowerCase(Locale.ENGLISH)) match {
      case Some("application/json") | Some("text/json") => play.api.mvc.BodyParsers.parse.json
      case Some("application/xml") | Some("text/xml") => play.api.mvc.BodyParsers.parse.xml
      case _ => play.api.mvc.BodyParsers.parse.error(Future.successful(UnsupportedMediaType("Invalid content type specified")))
    }
}

def test = Action(xmlOrJson) {
  request =>
    request.body match {
      case json: JsObject => Ok(json) //echo back posted json
      case xml: NodeSeq => Ok(xml) //echo back posted XML
    }
}

The xmlOrJson function looks at the content type request header to determine the body parser. If it is not xml or json then it returns the error body parser with an UnsupportedMediaType response (HTTP 415).

You then pass this function in as the body parser of any action you want to constrain to xml or json content. Within the action you can look at the body to determine if xml or json was parsed and process accordingly.



回答2:

You don't do it through annotation, but rather through your routes file, or through an if statement in your action. Depends on your use case as to which one is best suited.

The following URL gives you some information on the routes file for content negotiation. http://www.playframework.org/documentation/1.2.4/routes#content-types

Example

GET    /index.xml         Application.index(format:'xml')  
GET    /index.json        Application.indexJson(format:'json')  

The above calls different actions, but you could call the same action with a different format value if you wish.



回答3:

You may use default parser together with pattern matching in Play 2 with Scala. Something like this:

  def myAction() = Action { req =>
    req.body match {
      case AnyContentAsFormUrlEncoded(m) =>
        val f1 = m.get("field1").flatMap(_.headOption)
        ...
      case AnyContentAsJson(j) =>        
        val f1 = (j \ "field1").asOpt[String]
        ...
      case _ => //handle content types you don't support
        ...
}