【问题标题】:Play 2.4 ActionBuilder / ActionFunction, BodyParsers and JSON玩 2.4 ActionBuilder/ActionFunction、BodyParsers 和 JSON
【发布时间】:2016-04-14 08:44:44
【问题描述】:

在一个 Play 2.4 项目中,我创建了一个执行三件事的动作。

  1. 检查是否存在特定的标头。如果不是,则返回 HTTP 错误。
  2. 使用标头值对用户进行身份验证(身份验证函数将传递给该操作)。如果身份验证失败,则返回 HTTP 错误
  3. 将 JSON 主体解析为案例类,并将其提供给 Action 块代码。

为此,我使用了本页中解释的 Play Action 组合机制:https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition
但更具体地说,我想要的最终结果在这里解释: https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition#Putting-it-all-together

我写成功了:

package actions

import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.json._
import play.api.mvc.Results._
import play.api.mvc.{WrappedRequest, _}

import scala.concurrent.Future

object Actions {

  case class WithApiKeyRequest[A](apiKey: String, request: Request[A]) extends WrappedRequest[A](request)
  case class ParsedJsonRequest[A](parsed: Any, request: Request[A]) extends WrappedRequest[A](request)

  def AuthenticatedAndParsed[T, A](authencation: String => Future[_])(implicit reader: Reads[T]): ActionBuilder[ParsedJsonRequest] =
    WithApiKeyHeaderAction andThen AuthentificationAction(authencation) andThen JsonAction

  private[this] def WithApiKeyHeaderAction = new ActionBuilder[WithApiKeyRequest] {
    override def invokeBlock[A](request: Request[A], block: (WithApiKeyRequest[A]) => Future[Result]): Future[Result] =
      request.headers.get("ApiKey") match {
        case Some(apiKey: String) => block(WithApiKeyRequest(apiKey, request))
        case _                    => Future.successful { BadRequest(Json.obj("errors" -> "ApiKey header needed")) }
      }
  }

  private[this] def AuthentificationAction(authencationFunction: String => Future[_]) = new ActionFilter[WithApiKeyRequest] {
    override protected def filter[A](request: WithApiKeyRequest[A]): Future[Option[Result]] =
      authencationFunction(request.apiKey)
        .map { _ => None } // Do not filter the request
        .recover { case _ => Some(Unauthorized) }
  }

  private[this] def JsonAction[T](implicit reader: Reads[T]) = new ActionBuilder[ParsedJsonRequest] {
    composeParser(BodyParsers.parse.json)
    override def invokeBlock[A](request: Request[A], block: (ParsedJsonRequest[A]) => Future[Result]): Future[Result] = {
      request.body.asInstanceOf[JsValue].validate[T].fold(
        errors => Future { BadRequest(Json.obj("errors" -> JsError.toJson(errors))) },
        (parsedJson: T) => block(ParsedJsonRequest(parsedJson, request))
      )
    }
  }
}

它似乎运行良好,但并不完美,因为我不得不在 case class ParsedJsonRequest[A](parsed: Any, request: Request[A]) 中使用 Any 类型,因为我似乎不能这样做: case class ParsedJsonRequest[T, A](parsed: T, request: Request[A])

有可能这样做吗? 你认为我可以改进我的解决方案吗?怎么样?

我的问题不是关于如何进行动作组合。我了解它是如何工作的,并且我成功地编写了我的 ActionBuilders 和我想要的组合。 我的问题是关于如何改进我的作文。

谢谢
朱尔斯

【问题讨论】:

  • 不,因为这篇文章只解释了一个简单的动作组合案例,不回答我的问题。
  • 它是组合动作,使用BodyParser,所以概念相同,解决方案非常相似

标签: json scala parsing playframework playframework-2.4


【解决方案1】:

我不会为 JsonRequest 创建一个新的 ActionBuilder,而是简单地使用 AuthentificationAction ActionBuilder 并传递一个 json BodyParser

AuthentificationAction(parse.json) { 
  request => // Note that the request has type Request[JsValue]
    doStuffWithJson(request.body)
}

使用此构建器的任何操作都将获得Request[JsValue] 而不是Request[AnyContent]

【讨论】:

  • 我不觉得你的提议很有用。如果我这样做,代码将不太容易理解
猜你喜欢
  • 1970-01-01
  • 2023-04-05
  • 2015-10-02
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-13
  • 2016-02-07
相关资源
最近更新 更多