6

我通过覆盖 GlobalSettings 的 onRouteRequest 方法来拦截对我的播放应用程序的所有请求。现在,我需要从这里将一些数据发送到发送的操作,这样我就不会在所有操作中执行所有这些计算。如何为传递给超级 onRouteRequest 方法的请求 (play.api.mvc.RequestHeader) 对象设置属性?

4

1 回答 1

2

为了您的需要,我不会使用onRouteRequestwill 工作(至少优雅地)。

但是让我们尝试使用专用的结构进行拦截。

以下是您如何拦截请求、计算一些通用内容并将其传递给 Action

首先,这是一个Interceptor具有方法intercept和便捷方法的对象username

object Interceptor {

  def intercept[A, B](f: RequestHeader => Option[B], e: RequestHeader => Result)(action: B => Action[A]): Action[(Action[A], A)] = {

    val bodyParser = BodyParser {
      request =>
        f(request) map {
          b =>
            val innerAction = action(b)
            innerAction.parser(request).mapDone {
              body => body.right.map(innerBody => (innerAction, innerBody))
            }
        } getOrElse {
          Done(Left(e(request)), Input.Empty)
        }
    }

    Action(bodyParser) {
      request =>
        val (innerAction, innerBody) = request.body
        innerAction(request.map(_ => innerBody))
    }
  }

  def username[A](check: RequestHeader => Option[String]): ((String) => Action[A]) => Action[(Action[A], A)] = intercept(check, r => Results.Unauthorized("not logged in"))

}

如您所见,worker 函数intercept使您有机会根据请求内容计算一些东西。哪种类型的计算结果B可能会失败(Option),在这种情况下,有一个处理程序来告诉该做什么。

定义了要计算的内容后,您可以定义action使用接受 aB并给出. 的函数Action[A]

username方法只是一个简单的预定义拦截器,可以让我们定义如何检索登录的用户名,仅用于说明。

现在这是我们如何在您的Controller

  //index is defined for both GET and POST in routes, but fails on POST
  //  thanks to the interceptor that checks at first the used method
  //  the case mustn't be handled in the Action definition
  def index = Interceptor.intercept(
    /*check the method*/
    request => if (request.method == "GET") Some(request.method) else None,

    /*not a GET => bad request*/
    request => BadRequest(request.method + " not allowed")

  ) { /*the computation result*/method => Action {
      Ok("The method : " + method)
    }
  }

  //this controller retrieve the username in the session and renders it in a OK response
  def secured = Interceptor.username(r => r.session.get("username")) { username => Action {
      Ok("You're logged in as " + username)
    }
  }

  //this enables you to logged in => store in session
  def login(u:String) = Action { request => {
      Ok("Logged in as " + u) withSession(("username" -> u))
    }
  }

现在,如果您有通用计算,则可以创建预配置的拦截器(这里我使用的是案例类,但只需定义一个部分应用的函数interceptor就足够了)

  case class Intercept[B] (f: RequestHeader => Option[B], e: RequestHeader => Result) {

    def apply[A](action: B => Action[A]) = Interceptor.intercept[A,B](f, e)(action)

  }


  val getInterceptor = Intercept[String](
    request => if (request.method == "GET") Some(request.method) else None,
    request => BadRequest(request.method + " not allowed")
  )


  def index2 = getInterceptor { method => Action {
      Ok("Da method : " + method)
    }
  }

与评论相关的编辑

根据您的评论,以下是使用拦截器的方法(请注意,我已经模拟了主机检索和检查)

使用hostedand anotherHosted,您将能够测试此工作流程:

  • /hosted/false?host=myhost => 404 因为起初 myhost 没有被缓存,我为检查的模型提供了 false
  • /hosted/true?host=myhost => 不在缓存中但它会添加它,然后没有 404
  • /hosted/anotherHosted/false?host=myhost => 在缓存中,因为它是托管的 => 没有 404
  • /hosted/anotherHosted/false?host=notMyhost => 404

这是代码

def getHost(request:RequestHeader) = request.queryString.get("host").get.head
def checkHost(host:String, b: Boolean) = b

val checkHosted = (b: Boolean) => Intercept[String](
  request => {
    val host = getHost(request)
    Cache.getAs[String](host) match {
      case x@Some(_) => x
      case None => if (checkHost(host, b)) {
        Cache.set(host, host)
        Some(host)
      } else None
    }

  },
  request => NotFound(getHost(request) + "not hosted")
)

def hosted(b:String) = checkHosted(b.toBoolean) {
  host => Action {
    Ok("this host is ok : " + host)
  }
}
def anotherHosted(b:String) = checkHosted(b.toBoolean) {
  host => Action {
    Ok("this host is ok : " + host)
  }
}
于 2012-06-10T12:51:55.137 回答