Permalink
Browse files

Merge pull request #212 from julienrf/accept-helper

Helper to test request Accept content types
  • Loading branch information...
pk11 committed Apr 6, 2012
2 parents a18a8f7 + 2ec79c5 commit 63fea676526f989f67a4f661bfc251f71d80d1f8
@@ -164,6 +164,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.
*/
@@ -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.
@@ -60,6 +60,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.
*/
@@ -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)
+}
@@ -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
}
@@ -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
@@ -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
+ }
+ }
+
}
@@ -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();
+ }
+ }
}
@@ -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)
@@ -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.