Request Content-Type in Play! Framework for REST webservices - rest

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?

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.

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.

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
...
}

Related

Scala Finch post combinator: how to get the raw Request?

I need to log the raw request whenever I receive a particular post "Event" using Finch's post().
I have something like that:
val myEvent: Endpoint[String] = post("somepath" :: ipRangeEndpoint :: path[Long] :: jsonBody[Event]) {
(matchId: Long, event: Event) =>
(event match {
case _: Event.ToBeLogged =>
logger.debug(<REQUESTHERE>)
Ok("logged")
})
}
so let's say that, for a subset of cases, I need to log an incoming com.twitter.finagle.http.Request. How can I do that?
https://finagle.github.io/finch/user-guide.html#root-request
"It’s possible that Finch might be missing some of handy endpoints out of the box, especially that it’s evolved separately from Finagle. To overcome this and provide an extension point, there is a special endpoint instance, called root that returns a raw Finagle Request."

Create two or more APIs with same URL in play framework

I have use case where I need to read value from query string.
Currently I have two different APIs(Some other person has created the code) which maps to same URL
GET /service/class/:className/details controllers.Student.getStudentDetails(studentId)
GET /service/class/:className/details controllers.Student.getAllStudentsDetails()
If query string is present in URL then API1 should execute, otherwise API2.
As URL is same for both APIs, I am able to hit only get-student-details API(Because it has higher priority in routes file).
I am looking for alternatives to fix this problem.
As per my knowledge we don't need to create different APIs just to handle query strings.
I am thinking to merge 2 different APIs in single APIs which takes action depending upon presence of query string in request.
What I want to know is if there is way to execute two different APIs which maps to same URL(Only difference is with query string).
NOTE: I am using play 2.4.6.
I see few ways using a single controller function (say we chose getStudentDetails)
1) Having an Option parameter:
def getStudentDetails(studentId: Option[String]) = Action { studentId match {
case Some(id) => // do something
case None => // do something else
}
// ..
}
2) Look for your query string parameters inside your http request:
def getStudentDetails = Action { request =>
request.queryString.get("studentId") match {
case Some(list) => // do something...beware this is a List
case None => // do something else
}
//...
}

Extract Json from Spray POST as string, not by marshaling to entity

There is an existing question that has much of what I'm after:
Extracting Raw JSON as String inside a Spray POST route
But it stops short without explaining how to get the actual Json string representation out of the Directive[String]. I'm trying to send Json data to Kafka as a string (which the Kafka Producer serializes), so I'm trying to extract the Json in string form. I will do the marshalling to an entity at the other end in the Kafka consumer. The answer link above gets me close:
def rawJson = extract { _.request.entity.asString}
case "value2" => rawJson{ json =>// use the json }
But I end up with Directive[String]. How do I get the String out?
The example you referenced should work. You would use the rawJson directive they defined to wrap your inner route, and the json string would be made available within that inner route.
In the example below, personJson is a String, extracted by the body of the request via the rawJson directive, and made available to the inner route where the rest of the work is done.
def rawJson = extract { _.request.entity.asString}
val personRoute = {
(post & path("persons")){
rawJson{ personJson =>
onSuccess(personService.addPerson(person)){ personWithId =>
complete(StatusCodes.Created, personWithId)
}
}
}
I came up with the following syntax which accomplishes my need to extract the Json in String form. At first I thought it inefficient that I was unmarshaling and then remarshaling again, but then I realized that this provides a form of immediate Json validation. But there may be more efficient ways to do that.
The API is all Spray. handleWith uses an implicit conversion to the RawWeatherData case class.
path("weather"/"data"/"json") {
handleWith { rawRecord: RawWeatherData =>
val rawJsonStr = rawRecord.toJson.toString
kafkaJsonRecordIngest(rawJsonStr)
rawRecord
}
}

Spray routing: How to respond with different content-types?

In spray I would like to respond with different content-types, depending on the given Accept header. I've seen a couple of suggestions in the question by rompetroll, but I would like to hear if there are any canonical way of doing it (i. e. simple or already implemented).
In essence what I imagine should happen is something like:
path("somepath") {
get {
// Find whatever we would like to return (lazily)
...
// Marshall resource and complete depending on the `Accept` header
...
}
}
Thanks in advance.
See the tests in this commit.
I copied it here for reference:
case class Data(name: String, age: Int)
object Data {
import spray.json.DefaultJsonProtocol._
import spray.httpx.SprayJsonSupport._
// don't make those `implicit` or you will "ambiguous implicit" errors when compiling
val jsonMarshaller: Marshaller[Data] = jsonFormat2(Data.apply)
val xmlMarshaller: Marshaller[Data] =
Marshaller.delegate[Data, xml.NodeSeq](MediaTypes.`text/xml`) { (data: Data) ⇒
<data><name>{ data.name }</name><age>{ data.age }</age></data>
}
implicit val dataMarshaller: ToResponseMarshaller[Data] =
ToResponseMarshaller.oneOf(MediaTypes.`application/json`, MediaTypes.`text/xml`) (jsonMarshaller, xmlMarshaller)
}
You then using complete should suffice in your route, content-type negotiation is automatically taken care of:
get {
complete(Data("Ida", 83))
}
Spray is actually looking into the Accept header value and validates against it. So if route is returning application/json or text/plain and client accepts image/jpeg than spray will return 406 Not Acceptable. If client will request application/json ortext/plain from this route than he will receive repsonse with matching Content-Type.
The main trick here is to use correct marshallers for return objects.
You can read more about marshalling here.
Also you can override MediaType with respondWithMediaType directive, but I think it is better to use correct marshallers.

How to filter requests for access code in Play 2.1

I'd like to add a (per method / global) filter to requests, that simply rejects (404/403 page) any request that doesn't have a specific URL parameter.
I know Play has one-two mechanism to do this (e.g. register on Global.onRouteRequest()), so don't just send me a link to the documentation unless it contains a code sample that covers this question. I tried playing with the API but got a bit stuck.
Is this what you mean?
object Global extends WithFilters(AccessCheck)
object AccessCheck extends Filter with Results {
override def apply(next:RequestHeader => Result)(request:RequestHeader):Result =
request
.getQueryString("myCheck")
.map( myCheck => next(request))
.getOrElse(Forbidden)
}
http://www.playframework.com/documentation/2.1.0/ScalaInterceptors
If you are just trying to make some reusable code to filter requests on specific actions you my want to try creating an EssentialAction like this. This is known as action composition. This is what it would look like in the case you described. There is more information in the docs: http://www.playframework.com/documentation/2.1.1/ScalaActionsComposition
Note that you can do this in Play 2.0.X as well but EssentialAction doesn't exist, instead you use an Action, and there is just a little more syntax involved.
def RequireMyCheck(action: => EssentialAction): EssentialAction = {
EssentialAction { request =>
request
.getQueryString("myCheck")
.map( myCheck => action()(request))
.getOrElse(Forbidden)
}
}
You can use it like this:
def index = RequireMyCheck {
Action { request =>
Ok("Hello")
}
}