Skip to content
Browse files

Merge pull request #212 from julienrf/accept-helper

Helper to test request Accept content types
  • Loading branch information...
2 parents a18a8f7 + 2ec79c5 commit 63fea676526f989f67a4f661bfc251f71d80d1f8 @pk11 pk11 committed Apr 6, 2012
View
11 framework/src/play/src/main/java/play/mvc/Http.java
@@ -165,6 +165,17 @@ public static Context ctx() {
public abstract List<play.i18n.Lang> acceptLanguages();
/**
+ * @return The media types set in the request Accept header, not sorted in any particular order.
+ */
+ public abstract List<String> accept();
+
+ /**
+ * Check if this request accepts a given media type.
+ * @returns true if <code>mediaType</code> is in the Accept header, otherwise false
+ */
+ public abstract boolean accepts(String mediaType);
+
+ /**
* The query string content.
*/
public abstract Map<String,String[]> queryString();
View
2 framework/src/play/src/main/scala/play/api/mvc/Controller.scala
@@ -16,7 +16,7 @@ import play.api.http._
* }
* }}}
*/
-trait Controller extends Results with BodyParsers with Status with HeaderNames with ContentTypes {
+trait Controller extends Results with BodyParsers with Status with HeaderNames with ContentTypes with RequestExtractors {
/**
* Provides an empty `Action` implementation: the result is a standard ‘Not implemented yet’ result page.
View
19 framework/src/play/src/main/scala/play/api/mvc/Http.scala
@@ -61,6 +61,25 @@ package play.api.mvc {
}
/**
+ * @return The media types set in the request Accept header, not sorted in any particular order.
+ */
+ lazy val accept: Seq[String] = {
+ for {
+ acceptHeader <- headers.get(play.api.http.HeaderNames.ACCEPT).toSeq
+ value <- acceptHeader.split(",")
+ contentType <- value.split(";").headOption
+ } yield contentType
+ }
+
+ /**
+ * Check if this request accepts a given media type.
+ * @returns true if `mediaType` matches the Accept header, otherwise false
+ */
+ def accepts(mediaType: String): Boolean = {
+ accept.contains(mediaType) || accept.contains("*/*") || accept.contains(mediaType.takeWhile(_ != '/') + "/*")
+ }
+
+ /**
* The HTTP cookies.
*/
lazy val cookies: Cookies = Cookies(headers.get(play.api.http.HeaderNames.COOKIE))
View
59 framework/src/play/src/main/scala/play/api/mvc/RequestExtractors.scala
@@ -0,0 +1,59 @@
+package play.api.mvc
+
+trait RequestExtractors extends AcceptExtractors {
+
+ /**
+ * Convenient extractor allowing to apply two extractors.
+ * Example of use:
+ * {{{
+ * request match {
+ * case Accepts.Json() & Accepts.Html() => "This request accepts both JSON and HTML"
+ * }
+ * }}}
+ */
+ object & {
+ def unapply(request: RequestHeader): Option[(RequestHeader, RequestHeader)] = Some(request, request)
+ }
+
+}
+
+/**
+ * Define a set of extractors allowing to pattern match on the Accept HTTP header of a request
+ */
+trait AcceptExtractors {
+
+ /**
+ * Common extractors to check if a request accepts JSON, Html, etc.
+ * Example of use:
+ * {{{
+ * request match {
+ * case Accepts.Json() => Ok(toJson(value))
+ * case _ => Ok(views.html.show(value))
+ * }
+ * }}}
+ */
+ object Accepts {
+ val Json = Accepting("application/json")
+ val Html = Accepting("text/html")
+ val Xml = Accepting("application/xml")
+ val JavaScript = Accepting("application/javascript")
+ }
+
+}
+
+/**
+ * Convenient class to generate extractors checking if a given mime type matches the Accept header of a request.
+ * Example of use:
+ * {{{
+ * val AcceptsMp3 = Accepting("audio/mp3")
+ * }}}
+ * Then:
+ * {{{
+ * request match {
+ * case AcceptsMp3() => ...
+ * }
+ * }}}
+ */
+case class Accepting(val mimeType: String) {
+ def unapply(request: RequestHeader): Boolean = request.accepts(mimeType)
+}
View
8 framework/src/play/src/main/scala/play/core/j/JavaHelpers.scala
@@ -70,6 +70,10 @@ trait JavaHelpers {
req.queryString.mapValues(_.toArray).asJava
}
+ def accept = req.accept.asJava
+
+ def accepts(mediaType: String) = req.accepts(mediaType)
+
def cookies = new JCookies {
def get(name: String) = (for (cookie <- req.cookies.get(name))
yield new JCookie(cookie.name, cookie.value, cookie.maxAge, cookie.path, cookie.domain.getOrElse(null), cookie.secure, cookie.httpOnly)).getOrElse(null)
@@ -111,6 +115,10 @@ trait JavaHelpers {
def acceptLanguages = req.acceptLanguages.map(new play.i18n.Lang(_)).asJava
+ def accept = req.accept.asJava
+
+ def accepts(mediaType: String) = req.accepts(mediaType)
+
def queryString = {
req.queryString.mapValues(_.toArray).asJava
}
View
2 framework/src/play/src/test/scala/play/data/FormSpec.scala
@@ -11,6 +11,8 @@ class DummyRequest(data: Map[String, Array[String]]) extends play.mvc.Http.Reque
def path() = "test"
def host() = "localhost"
def acceptLanguages = new java.util.ArrayList[play.i18n.Lang]
+ def accept = List("text/html").asJava
+ def accepts(mediaType: String) = false
def headers() = new java.util.HashMap[String, Array[String]]()
def body() = new Http.RequestBody {
override def asFormUrlEncoded = data.asJava
View
8 framework/test/integrationtest/app/controllers/Application.scala
@@ -93,4 +93,12 @@ object Application extends Controller {
Ok("fromPath=%s fromQueryString=%s".format(fromPath, fromQueryString))
}
+ def accept = Action { request =>
+ request match {
+ case Accepts.Json() => Ok("json")
+ case Accepts.Html() => Ok("html")
+ case _ => BadRequest
+ }
+ }
+
}
View
9 framework/test/integrationtest/app/controllers/JavaApi.java
@@ -71,5 +71,14 @@ public static Result jsonpJava(String callback) {
return ok(jsonp(callback, json));
}
+ public static Result accept() {
+ if (request().accepts("application/json")) {
+ return ok("json");
+ } else if (request().accepts("text/html")) {
+ return ok("html");
+ } else {
+ return badRequest();
+ }
+ }
}
View
3 framework/test/integrationtest/conf/routes
@@ -36,6 +36,9 @@ GET /urldecode/:p controllers.Application.urldecode(p, q)
GET /javascript-routes controllers.Application.javascriptRoutes()
GET /javascript-test controllers.Application.javascriptTest(name)
+GET /accept controllers.Application.accept()
+GET /accept-java controllers.JavaApi.accept()
+
# Map static resources from the /public folder to the /public path
GET /public/*file controllers.Assets.at(path="/public", file)
View
30 framework/test/integrationtest/test/ApplicationSpec.scala
@@ -159,6 +159,36 @@ class ApplicationSpec extends Specification {
}
}
+ "test Accept header mime-types" in {
+ import play.api.http.HeaderNames._
+ "Scala API" in {
+ running(FakeApplication()) {
+ val url = controllers.routes.Application.accept().url
+ val Some(result) = routeAndCall(FakeRequest(GET, url).withHeaders(ACCEPT -> "text/html,application/xml;q=0.5"))
+ contentAsString(result) must equalTo ("html")
+
+ val Some(result2) = routeAndCall(FakeRequest(GET, url).withHeaders(ACCEPT -> "text/*"))
+ contentAsString(result2) must equalTo ("html")
+
+ val Some(result3) = routeAndCall(FakeRequest(GET, url).withHeaders(ACCEPT -> "application/json"))
+ contentAsString(result3) must equalTo ("json")
+ }
+ }
+ "Java API" in {
+ running(FakeApplication()) {
+ val url = controllers.routes.JavaApi.accept().url
+ val Some(result) = routeAndCall(FakeRequest(GET, url).withHeaders(ACCEPT -> "text/html,application/xml;q=0.5"))
+ contentAsString(result) must equalTo ("html")
+
+ val Some(result2) = routeAndCall(FakeRequest(GET, url).withHeaders(ACCEPT -> "text/*"))
+ contentAsString(result2) must equalTo ("html")
+
+ val Some(result3) = routeAndCall(FakeRequest(GET, url).withHeaders(ACCEPT -> "application/json"))
+ contentAsString(result3) must equalTo ("json")
+ }
+ }
+ }
+
}
}

0 comments on commit 63fea67

Please sign in to comment.
Something went wrong with that request. Please try again.