【问题标题】:How to pass a variable to an Action from an intercepted request in PlayFramework?如何从 PlayFramework 中截获的请求中将变量传递给 Action?
【发布时间】:2017-07-05 19:47:38
【问题描述】:

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

【问题讨论】:

  • 设置属性不可用,因为在函数式中,我们处于不可变的环境中。因此,例如,在向会话添加内容时,您可以使用withSession 创建“一个新的”。在 onRouteRequest 的上下文中,您无法创建新请求,因为您无法将其赋予基础操作
  • +100,同样的船,有一个数据 sn-p 注入到适用于所有路由/动作类型的请求中。我想在一个地方进行数据计算,onRouteRequest,然后在应用程序中隐式请求在范围内的任何地方都可以访问数据(而不是在各个地方重新计算它或为每个操作添加样板来处理它)。
  • @andypetrella scala 不是纯粹的函数式。我们可以通过 Action 组合和 WrappedRequest 将数据注入到 Request 中,有效地修改 Request 后路由。我非常喜欢在 onRouteRequest 中设置一个占位符 Map[String,String]。您可以复制请求,例如为 RequestHeader 上的“标签”映射提供一个值。当然玩,吹走你宝贵的数据,使用标签 Map 进行路由结果(控制器方法,类型 GET 等)
  • @virtualeyes 你是对的 Scala 不是纯粹的 f°。然而,Play 的内核最多使用函数范式,以便轻松实现并发等等。这就是为什么这样的事情在 Play 中是不可变的。但是,我不明白你的意思?你想要点什么?就地修改?
  • WrappedRequest 是 afaik 模拟向请求中注入数据的唯一方法。它工作得很好,而且工作得很好,只需要重构我的 Authenticate 操作以扩展一个新的 Task 操作特征,该特征将具有所需参数的案例类映射到 WrappedRequest。然后,在应用程序中的任何位置,而不是引用 play.api.mvc.Request,而是引用 com.company.Task,其中包含用户会话 ID,以及从 URI 提取/转换的数据

标签: scala playframework-2.0


【解决方案1】:

为了您的需要,我不认为使用 onRouteRequest 会起作用(至少优雅)。

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

以下是拦截请求、计算一些通用内容并将其传递给 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"))

}

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

定义了要计算的内容后,您可以使用一个函数定义您的 action,该函数接受 B 并给出 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)
    }
  }

EDIT与评论相关:

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

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

  • /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)
  }
}

【讨论】:

  • 谢谢安迪,让我告诉你确切的场景,我正在构建一个托管应用程序,用户可以注册并映射他们的域。当他们的用户/客户点击他们的映射域时,它首先检查该域是否由我们托管,否则返回 404 错误。如果域是托管的,我想将站点信息传递给所有操作,这样我就不会在它们中再次获取它。
猜你喜欢
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
  • 2019-09-11
  • 2020-10-17
  • 1970-01-01
  • 2015-08-24
  • 2021-12-27
  • 1970-01-01
相关资源
最近更新 更多