玩2.4 ActionBuilder / ActionFunction,BodyParsers和JSON

时间:2016-04-14 08:44:44

标签: json scala parsing playframework playframework-2.4

在Play 2.4项目中,我创建了一个可以做三件事的Action。

  1. 检查是否存在特定标头。如果不是,则返回HTTP错误。
  2. 使用标头值对用户进行身份验证(身份验证功能将传递给该操作)。如果auth失败则返回HTTP错误
  3. 将JSON主体解析为案例类,并将其提供给Action块代码。
  4. 为此,我在本页中使用了Play Action组合机制解释:https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition
    但更具体地说,我想要的最终结果在这里解释: https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition#Putting-it-all-together

    我成功地写了这个:

    { "errors": [ 
       { "code": 500, "reason": 500, 
      "content": 
        "com.orientechnologies.orient.core.exception.OSerializationException: Error on unmarshalling JSON content 
      '{\"transaction\":true,\"operations\":[{\"type\":\"c\",\"record\":
        {\"@class': content must be between { }" } ] }
    

    它似乎运作良好,但它并不完美,因为我强行使用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)) ) } } } 中的Any类型,因为我似乎无法做到这一点: case class ParsedJsonRequest[A](parsed: Any, request: Request[A])

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

    我的问题不是关于如何做动作组合。我理解它是如何工作的,我成功地写了我的ActionBuilders和我想要的作品。 我的问题是如何改善我的作文。

    感谢

1 个答案:

答案 0 :(得分:0)

我不是为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]