Skip to content

Commit

Permalink
Merge branch 'application-xml' of git://github.com/jroper/Play20 into…
Browse files Browse the repository at this point in the history
… jroper-application-xml

Conflicts:
	framework/test/integrationtest/test/ApplicationSpec.scala
  • Loading branch information
guillaumebort committed Feb 21, 2013
2 parents f0b22df + cb9e358 commit 5482966
Show file tree
Hide file tree
Showing 15 changed files with 70 additions and 31 deletions.
4 changes: 2 additions & 2 deletions documentation/manual/javaGuide/main/http/JavaBodyParsers.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ If you don't specify your own body parser, Play will use the default one guessin

- **text/plain**: `String`, accessible via `asText()`
- **application/json**: `JsonNode`, accessible via `asJson()`
- **text/xml**: `org.w3c.Document`, accessible via `asXml()`
- **application/xml**, **text/xml** or **application/XXX+xml**: `org.w3c.Document`, accessible via `asXml()`
- **application/form-url-encoded**: `Map<String, String[]>`, accessible via `asFormUrlEncoded()`
- **multipart/form-data**: `Http.MultipartFormData`, accessible via `asMultipartFormData()`
- Any other content type: `Http.RawBuffer`, accessible via `asRaw()`
Expand Down Expand Up @@ -96,4 +96,4 @@ public static Result index() {
}
```

> **Next:** [[Actions composition | JavaActionsComposition]]
> **Next:** [[Actions composition | JavaActionsComposition]]
8 changes: 4 additions & 4 deletions documentation/manual/javaGuide/main/xml/JavaXmlRequests.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Handling an XML request

An XML request is an HTTP request using a valid XML payload as request body. It must specify the `text/xml` MIME type in its `Content-Type` header.
An XML request is an HTTP request using a valid XML payload as request body. It must specify the `application/xml` or `text/xml` MIME type in its `Content-Type` header.

By default, an action uses an **any content** body parser, which you can use to retrieve the body as XML (actually as a `org.w3c.Document`):

Expand Down Expand Up @@ -47,7 +47,7 @@ You can test it with **cURL** on the command line:

```
curl
--header "Content-type: text/xml"
--header "Content-type: application/xml"
--request POST
--data '<name>Guillaume</name>'
http://localhost:9000/sayHello
Expand Down Expand Up @@ -88,10 +88,10 @@ Now it replies with:

```
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Type: application/xml; charset=utf-8
Content-Length: 46
<message status="OK">Hello Guillaume</message>
```

> **Next:** [[Handling file upload | JavaFileUpload]]
> **Next:** [[Handling file upload | JavaFileUpload]]
4 changes: 2 additions & 2 deletions documentation/manual/scalaGuide/main/http/ScalaBodyParsers.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ This body parser checks the `Content-Type` header and decides what kind of body

- **text/plain**: `String`
- **application/json**: `JsValue`
- **text/xml**: `NodeSeq`
- **application/xml**, **text/xml** or **application/XXX+xml**: `NodeSeq`
- **application/form-url-encoded**: `Map[String, Seq[String]]`
- **multipart/form-data**: `MultipartFormData[TemporaryFile]`
- any other content type: `RawBuffer`
Expand Down Expand Up @@ -149,4 +149,4 @@ def save = Action(maxLength(1024 * 10, parser = storeInUserFile)) { request =>
}
```

> **Next:** [[Action composition | ScalaActionsComposition]]
> **Next:** [[Action composition | ScalaActionsComposition]]
4 changes: 2 additions & 2 deletions documentation/manual/scalaGuide/main/http/ScalaResults.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Will automatically set the `Content-Type` header to `text/plain`, while:
val xmlResult = Ok(<message>Hello World!</message>)
```

will set the Content-Type header to `text/xml`.
will set the Content-Type header to `application/xml`.

> **Tip:** this is done via the `play.api.http.ContentTypeOf` type class.
Expand Down Expand Up @@ -97,4 +97,4 @@ def HTML(implicit codec: Codec) = {

You can do the same in your API if you need to handle the charset in a generic way.

> **Next:** [[Session and Flash scopes | ScalaSessionFlash]]
> **Next:** [[Session and Flash scopes | ScalaSessionFlash]]
8 changes: 4 additions & 4 deletions documentation/manual/scalaGuide/main/xml/ScalaXmlRequests.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Handling an XML request

An XML request is an HTTP request using a valid XML payload as the request body. It must specify the `text/xml` MIME type in its `Content-Type` header.
An XML request is an HTTP request using a valid XML payload as the request body. It must specify the `application/xml` or `text/xml` MIME type in its `Content-Type` header.

By default an `Action` uses a **any content** body parser, which lets you retrieve the body as XML (actually as a `NodeSeq`):

Expand Down Expand Up @@ -38,7 +38,7 @@ You can test it with **cURL** from a command line:

```
curl
--header "Content-type: text/xml"
--header "Content-type: application/xml"
--request POST
--data '<name>Guillaume</name>'
http://localhost:9000/sayHello
Expand Down Expand Up @@ -72,10 +72,10 @@ Now it replies with:

```
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Type: application/xml; charset=utf-8
Content-Length: 46
<message status="OK">Hello Guillaume</message>
```

> **Next:** [[Handling file upload | ScalaFileUpload]]
> **Next:** [[Handling file upload | ScalaFileUpload]]
2 changes: 1 addition & 1 deletion framework/src/play/src/main/java/play/mvc/BodyParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public play.api.mvc.BodyParser<Http.RequestBody> parser(int maxLength) {
}

/**
* Parse the body as Xml if the Content-Type is text/xml.
* Parse the body as Xml if the Content-Type is application/xml.
*/
public static class Xml implements BodyParser {
public play.api.mvc.BodyParser<Http.RequestBody> parser(int maxLength) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ trait DefaultContentTypeOfs {
}

/**
* Default content type for `Xml` values (`text/xml`).
* Default content type for `Xml` values (`application/xml`).
*/
implicit def contentTypeOf_Xml(implicit codec: Codec): ContentTypeOf[Xml] = {
ContentTypeOf[Xml](Some(ContentTypes.XML))
Expand Down Expand Up @@ -73,14 +73,14 @@ trait DefaultContentTypeOfs {
}

/**
* Default content type for `NodeSeq` values (`text/xml`).
* Default content type for `NodeSeq` values (`application/xml`).
*/
implicit def contentTypeOf_NodeSeq[C <: scala.xml.NodeSeq](implicit codec: Codec): ContentTypeOf[C] = {
ContentTypeOf[C](Some(ContentTypes.XML))
}

/**
* Default content type for `NodeBuffer` values (`text/xml`).
* Default content type for `NodeBuffer` values (`application/xml`).
*/
implicit def contentTypeOf_NodeBuffer(implicit codec: Codec): ContentTypeOf[scala.xml.NodeBuffer] = {
ContentTypeOf[scala.xml.NodeBuffer](Some(ContentTypes.XML))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ trait MimeTypes {
/**
* Content-Type of xml.
*/
val XML = "text/xml"
val XML = "application/xml"

/**
* Content-Type of css.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ object MimeTypes {
xlv=application/excel
xlw=application/excel
xm=audio/xm
xml=text/xml
xml=application/xml
xmz=xgl/movie
xpi=application/x-xpinstall
xpix=application/x-vndls-xpix
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ sealed trait AnyContent {
}

/**
* text/xml
* application/xml
*/
def asXml: Option[NodeSeq] = this match {
case AnyContentAsXml(xml) => Some(xml)
Expand Down Expand Up @@ -260,6 +260,8 @@ trait BodyParsers {
*/
val UNLIMITED: Int = Integer.MAX_VALUE

private val ApplicationXmlMatcher = """application/.*\+xml.*""".r

/**
* Default max length allowed for text based body.
*
Expand Down Expand Up @@ -411,18 +413,19 @@ trait BodyParsers {
def tolerantXml: BodyParser[NodeSeq] = tolerantXml(DEFAULT_MAX_TEXT_LENGTH)

/**
* Parse the body as Xml if the Content-Type is text/xml.
* Parse the body as Xml if the Content-Type is application/xml, text/xml or application/XXX+xml.
*
* @param maxLength Max length allowed or returns EntityTooLarge HTTP response.
*/
def xml(maxLength: Int): BodyParser[NodeSeq] = when(
_.contentType.exists(_.startsWith("text/xml")),
_.contentType.exists(t => t.startsWith("text/xml") || t.startsWith("application/xml")
|| ApplicationXmlMatcher.pattern.matcher(t).matches()),
tolerantXml(maxLength),
request => Play.maybeApplication.map(_.global.onBadRequest(request, "Expecting text/xml body")).getOrElse(Results.BadRequest)
request => Play.maybeApplication.map(_.global.onBadRequest(request, "Expecting xml body")).getOrElse(Results.BadRequest)
)

/**
* Parse the body as Xml if the Content-Type is text/xml.
* Parse the body as Xml if the Content-Type is application/xml, text/xml or application/XXX+xml.
*/
def xml: BodyParser[NodeSeq] = xml(DEFAULT_MAX_TEXT_LENGTH)

Expand Down Expand Up @@ -515,7 +518,7 @@ trait BodyParsers {
Logger("play").trace("Parsing AnyContent as text")
text(request).map(_.right.map(s => AnyContentAsText(s)))
}
case Some("text/xml") => {
case Some("text/xml") | Some("application/xml") | Some(ApplicationXmlMatcher()) => {
Logger("play").trace("Parsing AnyContent as xml")
xml(request).map(_.right.map(x => AnyContentAsXml(x)))
}
Expand Down
6 changes: 3 additions & 3 deletions framework/src/play/src/main/scala/play/api/mvc/Results.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ sealed trait WithHeaders[+A <: Result] {
*
* For example:
* {{{
* Ok("<text>Hello world</text>").as("text/xml")
* Ok("<text>Hello world</text>").as("application/xml")
* }}}
*
* @param contentType the new content type.
Expand Down Expand Up @@ -312,7 +312,7 @@ trait PlainResult extends Result with WithHeaders[PlainResult] {
*
* For example:
* {{{
* Ok("<text>Hello world</text>").as("text/xml")
* Ok("<text>Hello world</text>").as("application/xml")
* }}}
*
* @param contentType the new content type.
Expand Down Expand Up @@ -534,7 +534,7 @@ case class AsyncResult(result: Future[Result]) extends Result with WithHeaders[A
*
* For example:
* {{{
* Ok("<text>Hello world</text>").as("text/xml")
* Ok("<text>Hello world</text>").as("application/xml")
* }}}
*
* @param contentType the new content type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ class Xml(text: String) extends Appendable[Xml] with Content with play.mvc.Conte
override def toString = buffer.toString

/**
* Content type of XML (`text/xml`).
* Content type of XML (`application/xml`).
*/
def contentType = "text/xml"
def contentType = "application/xml"

def body = toString

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,12 @@ object Application extends Controller {
def routetest(parameter: String) = Action {
Ok("")
}

def anyXml = Action { request =>
request.body.asXml.map(xml => Ok(xml)).getOrElse(NotFound("Not XML"))
}

def xml = Action(parse.xml) { request =>
Ok(request.body)
}
}
3 changes: 3 additions & 0 deletions framework/test/integrationtest/conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ GET /ident/:è27 controllers.πø$7ß.ôü65$t(è27: Int)
GET /hello controllers.Application.hello()
GET /setLang controllers.Application.setLang(lang)

POST /any-xml controllers.Application.anyXml
POST /xml controllers.Application.xml

-> /module module.Routes

GET /routes controllers.Application.route(abstract)
Expand Down
25 changes: 25 additions & 0 deletions framework/test/integrationtest/test/ApplicationSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,31 @@ class ApplicationSpec extends Specification {
route._2 must_== "/read/$name<[^/]+>"
route._3 must startWith("controllers.JavaApi.readCookie")
}

"support xml" in {
"detect xml when content type is application/xml" in new WithServer() {
await(wsUrl("/any-xml").withHeaders("Content-Type" -> "application/xml").post(<foo>bar</foo>)).status must_== 200
}
"detect xml when content type is text/xml" in new WithServer() {
await(wsUrl("/any-xml").withHeaders("Content-Type" -> "text/xml").post(<foo>bar</foo>)).status must_== 200
}
"detect xml when content type is application/atom+xml" in new WithServer() {
await(wsUrl("/any-xml").withHeaders("Content-Type" -> "application/atom+xml").post(<foo>bar</foo>)).status must_== 200
}
"accept xml when content type is application/xml" in new WithServer() {
await(wsUrl("/xml").withHeaders("Content-Type" -> "application/xml").post(<foo>bar</foo>)).status must_== 200
}
"accept xml when content type is text/xml" in new WithServer() {
await(wsUrl("/xml").withHeaders("Content-Type" -> "text/xml").post(<foo>bar</foo>)).status must_== 200
}
"accept xml when content type is application/atom+xml" in new WithServer() {
await(wsUrl("/xml").withHeaders("Content-Type" -> "application/atom+xml").post(<foo>bar</foo>)).status must_== 200
}
"send xml responses as application/xml" in new WithServer() {
await(wsUrl("/xml").withHeaders("Content-Type" -> "application/xml").post(<foo>bar</foo>)).header("Content-Type").get must startWith("application/xml")
}
}

}

}

0 comments on commit 5482966

Please sign in to comment.