Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access Request Body in essential filter Play Framework 2

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)
      }
    }
  }
}
like image 878
Mohsen Kashi Avatar asked Mar 14 '15 09:03

Mohsen Kashi


2 Answers

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.

like image 198
johanandren Avatar answered Oct 13 '22 22:10

johanandren


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 {...
like image 1
searchrome Avatar answered Oct 14 '22 00:10

searchrome