Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Helper to test request Accept content types
- Loading branch information
Showing
10 changed files
with
150 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
framework/src/play/src/main/scala/play/api/mvc/RequestExtractors.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2ec79c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess browsers often send a
*/*
accepts header as fallback:Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Which means that a construct like:
case Accepts.Json() => Ok("json")
Will usually match as well...
Edit:
The
qvalue
of the accept header is not taken into account as well. Which is part of the problem I guess. Instead of writing a single match statement you would have to loop over the accept types (in order of qvalue) and see if we can actually serve this content. If none of that is matched, you'd still want to have a fallback I guess.I'd make a pull request but I can't really come up with a nice way of writing this. Since my Scala isn't up to par with yours, maybe you guys have an idea yourself?
2ec79c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, Scala pattern matching tries the cases in the order they are defined, so you can write the following:
So browser’s requests will always be caught by the first case.
2ec79c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still, if I would rather have
Json
overXML
(or the other way around) this would be a problem. Don't get me wrong, this is a good feature to have. However I think that in general the client is the driving force between what kind of content he would have. Determined not only by the type, but also the qvalue. Now it's the controller that determines the order instead.I kind of feel this is such a thing where you would afterwards think: I wish I would have done the initial implementation differently because now we have API breaking changes (for example).
Again, just trying to give some positive criticism here.
2ec79c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with aukevanleeuwen - really, the pattern matching should take into account the qvalues in determining what content type to return. Are there any plans to support this?
2ec79c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, but I don't think it is possible with pattern matching. I'm however working on another way to solve this problem.
2ec79c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the same impression than Julien. The pattern matching is pretty much a local thing. In order to evaluate based on the q-values your need a global solution. Based on the new helper functions in 2.1 I build this (https://gist.github.com/4594246). It works but the syntax is quite ugly. Still it could be improved with a marco wrapping the statement into a function (making the statement lazily evaluated).
2ec79c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, in 2.1 an API similar to your snippet has been introduced: https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/mvc/Render.scala
Example of use: https://github.com/playframework/Play20/blob/master/framework/test/integrationtest/app/controllers/Application.scala#L137-L143
Please share your feedback with us.