Access Request Body in essential filter Play Framework 2
Asked Answered
M

2

6

I am new to Scala. As mentioned in play framework official documentation in https://www.playframework.com/documentation/2.3.x/ScalaHttpFilters :

Play provides a lower level filter API called EssentialFilter which gives you full access to the body of the request.

but there is not any method to accessing request body in requestHeader object.

import play.api.Logger
import play.api.mvc._
import play.api.libs.concurrent.Execution.Implicits.defaultContext

object LoggingFilter extends EssentialFilter {
  def apply(nextFilter: EssentialAction) = new EssentialAction {
    def apply(requestHeader: RequestHeader) = {
      val startTime = System.currentTimeMillis
      nextFilter(requestHeader).map { result =>
        val endTime = System.currentTimeMillis
        val requestTime = endTime - startTime
        Logger.info(s"${requestHeader.method} ${requestHeader.uri}" +
          s" took ${requestTime}ms and returned ${result.header.status}")
        result.withHeaders("Request-Time" -> requestTime.toString)
      }
    }
  }
}
Medication answered 14/3, 2015 at 9:44 Comment(4)
You have access to the query string, the parameters and headers. Which kind of info would you like to get from there?Knothole
The body of request , all HTTP methods support body ( expect GET). and I need to intercept request bodiesMedication
Possible duplicate of this question.Sememe
I see that! that is for response body, Responses are different from requests!Medication
B
5

The abstract method EssentialFilter.apply that you implement when you create an EssentialFilter returns an EssentialAction which basically is a function that goes from RequestHeader to an Iteratee[Array[Byte], Result] into which play will feed the incoming byte chunks of the http body.

If you aren't familiar with the iteratee API, the signature above basically means, a thing that will accept chunks of data of the type Array[Byte] and sooner or later produce a Result out of those.

The normal play Action's is a subclass of EssentialAction that parses the body using a BodyParser and then feeding the result of that (Request which is both request headers and the parsed body) into a function that in turn returns a Future[Result]

So if you only have one filter, then next: EssentialAction in your filter is basically the actual controller action. That you get to take its Iteratee[Array[Bytes], Result] and wrap it with something is what makes it possible to access the body of the request, before the body parser has gotten to touch it.

So to achieve what you want will require you to learn a bit about how Iteratees works and how to use Enumeratees to transform or peek into data fed into an iteratee.

Some starting points

The play framework docs has got some pretty good information about iteratees: https://www.playframework.com/documentation/2.3.x/Iteratees

There is also a nice blog article by James Roper (play tech lead) that might help: https://jazzy.id.au/2012/11/06/iteratees_for_imperative_programmers.html

Important note

How filters works in play makes it impossible to look at the parsed body with a filter. Unless you make an enumeratee that will parse the body but still pass the bytes on to the actual action (this will have you parsing the body twice).

If this is what you want you may be better off working with the ActionBuilder and creating your own custom Action that will allow you to look at the parsed request.

Burning answered 15/3, 2015 at 18:59 Comment(3)
I'm working on an EssentialFilter which should introspect URL-encoded forms and perform actions based on specific values in this form. I want to do this for all requests, as I want to implement the equivalent of: guides.rubyonrails.org/… I was thinking of using: BodyParsers.parse.urlFormEncoded.apply(request) for this, however it is unclear what is the best way to consume the iteratee, introspect the form and then forward it. Would you have an example of how to do this Johan?Ruthenian
Sorry, nothing from the top of my head. Ask on the playframework gitter channel or mailing list!Burning
all is nice but would be much nicer to show an example in the answer itself.Acariasis
T
1

you don't want to parse request body on filtering level because of the body not parsed on filtering process, you have to buffer, parse and stream so it is better to use action composition

    object LoggingAction extends ActionBuilder[Request] {
      def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
          Logger.debug(request.body.asInstanceOf[AnyContentAsJson].json.toString())    
          block(request)
  }
}

on controller side use like

 def index = LoggingAction {...
Tswana answered 15/11, 2016 at 13:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.