Play 2 - Set header on all responses?

2019-02-16 06:41发布

问题:

I'm aware from Setting HTTP headers in Play 2.0 (scala)? that you can set response headers on a case-by-case basis by doing, for example, Ok("hello").withHeaders(PRAGMA -> "no-cache").

What if you want to set that header, or a few different headers, on responses from all your Actions? You wouldn't want to repeat the withHeaders everywhere. And since this is more like an application-wide configuration, you might not want Action writers to have to use a different syntax to get your headers (e.g. OkWithHeaders(...))

What I have now is a base Controller class that looks like

class ContextController extends Controller {
 ...
 def Ok(h: Html) = Results.Ok(h).withHeaders(PRAGMA -> "no-cache")
}

but that doesn't feel quite right. It feels like there should be more of an AOP-style way of defining the default headers and having them added to each response.

回答1:

In your Global.scala, wrap every call in an action:

import play.api._
import play.api.mvc._
import play.api.Play.current
import play.api.http.HeaderNames._

object Global extends GlobalSettings {

  def NoCache[A](action: Action[A]): Action[A] = Action(action.parser) { request =>
    action(request) match {
      case s: SimpleResult[_] => s.withHeaders(PRAGMA -> "no-cache")
      case result => result
    }
  }

  override def onRouteRequest(request: RequestHeader): Option[Handler] = {
    if (Play.isDev) {
      super.onRouteRequest(request).map {
        case action: Action[_] => NoCache(action)
        case other => other
      }
    } else {
      super.onRouteRequest(request)
    }
  }

}

In this case, I only call the action in dev mode, which makes most sense for a no-cache instruction.



回答2:

The topic is quite old now, but with Play 2.1 it is even simpler now. Your Global.scala class should look like this :

import play.api._
import play.api.mvc._
import play.api.http.HeaderNames._

/**
 * Global application settings.
 */
object Global extends GlobalSettings {

  /**
   * Global action composition.
   */
  override def doFilter(action: EssentialAction): EssentialAction = EssentialAction { request =>
    action.apply(request).map(_.withHeaders(
      PRAGMA -> "no-cache"
    ))
  }
}


回答3:

The easiest way to achieve fine-grained control is in using wrapped actions. In your case it can be something like that:

object HeaderWriter {
    def apply(f: Request[AnyContent] => SimpleResult):Action[AnyContent] = {
        Action { request =>
            f(request).withHeaders(PRAGMA -> "no-cache")
        }
    }
}

and use it in such manner:

def doAction = HeaderWriter { request =>
    ... do any stuff your want ...
    Ok("Thats it!")
}


回答4:

There are too ways. You can use action-composition. Then you must declare at every Controller that you want set here the header. Another option is to use the GlobalSettings. There are similar solutions for java, too.