scalaplayframework

Scala Play get response headers in Filter Middleware?


I want to capture the response headers that my server is going to send with response headers to the browser. Upon inspection, I can see those: enter image description here

However when I try to tap into those using my Filter Middleware:

class CustomFilter @Inject()(implicit val mat: Materializer, ec: ExecutionContext) extends Filter {
  private val logger = Logger(this.getClass)

  private def getRequestContext(ctx: RequestContext):String = {
    val RequestContext(userAgent, remoteAddress) = ctx
    // Log request context information based on availability of remoteAddress, appToken and appVersion
    s"NRS Request [User-Agent=$userAgent] [Remote-Address=$remoteAddress]"
  }

  private def getResponseContext(resCtx: ResponseContext): String = {
    val ResponseContext(duration, statusCode, appToken, appVersion, requestID, clientID, redirect, via) = resCtx
    s"NRS Response [DurationInMs=$duration] [Status=$statusCode] [App-Token=$appToken] [App-Version=$appVersion] [Request-ID=$requestID] [Client-ID=$clientID] [Redirect=$redirect] [Via=$via]"
  }


  private def getCompleteContext(reqCtx: RequestContext, resCtx: ResponseContext):String =
    s"{{${getRequestContext(reqCtx)}::${getResponseContext(resCtx)}}}"

  override def apply(nextFilter: RequestHeader => Future[Result])(requestHeader: RequestHeader): Future[Result] = {
    val startTime = System.currentTimeMillis()

    // Extract request context information
    val userAgent = requestHeader.headers.get("User-Agent").getOrElse("")
    val remoteAddress = requestHeader.headers.get("Remote-Address")

    nextFilter(requestHeader).flatMap { result =>
      val endTime = System.currentTimeMillis()
      val duration = endTime - startTime
      val status = result.header.status
      val appToken = result.header.headers.get("X-App-Token")
      val appVersion = result.header.headers.get("X-App-Version")
      val requestID = result.header.headers.get("X-Request-ID")
      val clientID = result.header.headers.get("X-Client-ID")
      val redirect = result.header.headers.get("X-Redirect")
      val via = result.header.headers.get("Via")

      println(s"""Headers: ${result.header.headers.mkString(",")}::${requestHeader.headers.headers.mkString(",")}""")

      if (userAgent.nonEmpty) {
        logger.warn(
          getCompleteContext(
            RequestContext(userAgent, remoteAddress),
            ResponseContext(duration, status, appToken, appVersion, requestID, clientID, redirect, via)
          )
        )
      }

      Future.successful(result)
    } recoverWith {
      case e: Throwable =>
        val endTime = System.currentTimeMillis()
        val duration = endTime - startTime

        if (userAgent.nonEmpty) {
          logger.warn(getCompleteContext(RequestContext(userAgent, remoteAddress), ResponseContext(duration, 500, None, None, None, None, None, None)))
        }

        // Propagate the exception
        Future.failed(e)
    }
  }
}

I am not able to see those response headers in my middleware, unfortunately. Can someone help me understand what am I doing wrong?

I am able to set new headers but not getting old headers: Future.successful(result.withHeaders("X-Request-ID-022" -> requestID.getOrElse("dvcsdf")))

I get these response logs:

Response Header 121 - Access-Control-Allow-Origin: *
Response Header 121 - ETag: "XXXXXXX"

Here are the common Filters that I have applied:

class Filters @Inject()(corsFilter: CORSFilter,
                        clientIdFilter: ClientIdFilter,
                        requestIdFilter: RequestIdFilter,
                        processingTimeFilter: ProcessingTimeFilter,
                        host: HostFilter,
                        date: DateFilter,
                        head: HeadFilter,
                        gzipFilter: GzipFilter,
                        customFilter: CustomFilter
                       )
  extends DefaultHttpFilters(Seq(corsFilter, processingTimeFilter,
      clientIdFilter, requestIdFilter, host, date, head, gzipFilter,customFilter):_*)

Solution

  • Your filter needs to be at the start of the list. The code that applies the filters wraps them in the same order as the list, so basically expands out to this:

    firstFilter.apply(secondFilter.apply(thirdFilter.apply(action))))
    

    So, the first filter is the outer most filter, so when it gets a response back, it will get it from all the inner filters with the headers set by all the inner filters.