Scala EssentialAction:如何在不解析请求体的情况下获取请求体

Scala EssentialAction:如何在不解析请求体的情况下获取请求体,scala,playframework,Scala,Playframework,鉴于以下要点 object MyController extends Controller { ... def HasToken(action: Token => EssentialAction) = EssentialAction { request => ... // this doesn't compile val body = request.body match { case json: JsValue => jso

鉴于以下
要点

object MyController extends Controller {

  ...

  def HasToken(action: Token => EssentialAction) = EssentialAction { request =>

    ...

    // this doesn't compile
    val body = request.body match {
      case json: JsValue => json.toString
      case _ => ""
    }

    // calculate hash with body content here
    ...
  }

  // here is an authenticated action
  def getUser(userId: Strign) = HasToken { token =>
    Action(parse.json) { request =>
      request.body.validate[User] match {
        ...
      }
    }
  }
}
。。。如何在不解析请求体的情况下获取请求体

我不想也不需要在
HasToken
中解析请求主体,因为主体将在action
getUser
中解析。我只需要主体的原始内容来计算散列


HasToken
中的代码无法编译,因为
request
属于
RequestHeader
类型,而我需要一个
request
,它定义了
body

这对您有效吗

object MyController extends Controller {

  // Your HasToken Action
  def Authenticate(action: Token => EssentialAction) = EssentialAction { requestHeader =>
    // ... execute logic to verify authenticity using requestHeader
  }

  // Your action to validate tampering of request body and validity of JSON
  def Validate[A](action: Token => Request[A]) = Action(parse.json) { request =>
    val body = request.body
    body match {
      case json: JsValue => json.toString
      case _ => ""
    }
    // calculate hash with body content here

    body.validate[User] match {
      // ...
    }
  }

  def getUser(userId: Strign) = Authenticate { token =>
    Validate { user =>
      //.... Continue
    }
  }
}
  • 身份验证仅使用RequestHeader
  • 验证使用请求体。(额外奖励:正文只解析一次)
编辑:

问题#1:我不想在验证中验证正文。。。因为我需要一个通用的验证机制,它可以在任何地方使用,而不考虑内容类型(例如用户、消息等)

如何添加另一个类型参数(使其成为泛型):

问题#2:此外,如果令牌验证失败,则不必处理正文(这在文件上载的情况下很重要,只有在验证成功时才需要执行文件上载)。这样,在我看来,最好的选择是在Validate中读取主体的原始内容

这很容易做到:

def Validate[A, B](action: Token => Request[A])(implicit reads: Reads[B]) = Action(parse.json) { request =>
   val body = request.body
    body match {
      case json: JsValue => json.toString
      case _ => ""
    }
    // calculate hash with body content here and figure out if the body is tampered
    if (bodyIsNotTampered) {
      body.validate[B] match {
        // ...
      }
    } else {
      // log and return Future.successful(BadRequest)
    }
  }
编辑3:完整解决方案:

import play.api.libs.json.{Json, JsValue, Format}

object CompilationUtils {
  class Token
  case class User(name: String)
  implicit val UserFormat = Json.format[User]

  def authenticate = new Token // authentication logic

  def isTampered(body: JsValue) = {
    val bodyAsStr: String = Json.stringify(body)
    // calculate hash with body content here
    false
  }

}

object MyController extends Controller {
  import CompilationUtils._

  // Your HasToken Action
  def Authenticate(action: Token => EssentialAction) = EssentialAction { requestHeader =>
    action(authenticate)(requestHeader) // your execute logic to verify authenticity using requestHeader
  }

  // Your action to validate tampering of request body and validity of JSON
  def Validate[A, B](request: Request[A])(implicit formatA: Format[A], formatB: Format[B]): Either[Result, B] = {
    val body = request.body
    val bodyAsJsValue = Json.toJson(body)
    if (!isTampered(bodyAsJsValue)) {
      bodyAsJsValue.validate[B].fold(
        valid   = res => Right(res),
        invalid = err => Left(BadRequest(err.toString))
      )
    } else {
      Left(BadRequest) // Request Tampered
    }
  }

  def getUser(userId: String) = Authenticate { token =>
    Action(parse.json) { request =>
      Validate(request).fold(
        badReq => badReq,
        user   =>
          // continue...
          Ok("")
      )
    }
  }
}

HasToken的职责不明确。hasToken逻辑在哪里?我只看到您正在尝试访问主体以计算有效负载哈希。你想要什么也不清楚。你想编译它吗?如果是这样,我必须问为什么您的HasToken签名是Token=>EssentialAction而不是Token=>Request。。如果希望HasToken不解析正文,那么如果您需要,为什么不在RequestHeader中发送令牌呢?
HasToken
验证请求标头是否具有有效的JSON Web令牌。。。散列用于验证请求未被篡改。不管怎么说,这些细节对我的问题是没有用的。我只是问如何获取请求的原始内容(我需要它来计算散列,散列必须与令牌中包含的散列相对应)。“无论如何,这些细节对我的问题没有用处。”=>如果你接受我的回答,我不这么认为:)谢谢你的回答。我不想在
validate
中验证主体。。。因为我需要一个通用的验证机制,它可以在任何地方使用,而不考虑内容类型(例如用户、消息等)。此外,如果令牌验证失败,则不必处理主体(这在文件上载的情况下很重要,只有在验证成功的情况下才能执行文件上载)。这样的话,在我看来,最好的选择是在
Validate
@j3d中阅读主体的原始内容:我处理了您的评论。请看一下最新的答案。。。。问题是解析器可能会根据请求而有所不同。例如,如果主体包含Json,则为
parse.Json
,但如果是多部分/表单数据(即文件上载),则解析器为
fsBodyParser
,一种将传入数据直接存储到MongoDB中的自定义解析器。@j3d:将BodyParser传递给验证操作会有什么问题?Venkat,非常感谢您的大力支持;-)
import play.api.libs.json.{Json, JsValue, Format}

object CompilationUtils {
  class Token
  case class User(name: String)
  implicit val UserFormat = Json.format[User]

  def authenticate = new Token // authentication logic

  def isTampered(body: JsValue) = {
    val bodyAsStr: String = Json.stringify(body)
    // calculate hash with body content here
    false
  }

}

object MyController extends Controller {
  import CompilationUtils._

  // Your HasToken Action
  def Authenticate(action: Token => EssentialAction) = EssentialAction { requestHeader =>
    action(authenticate)(requestHeader) // your execute logic to verify authenticity using requestHeader
  }

  // Your action to validate tampering of request body and validity of JSON
  def Validate[A, B](request: Request[A])(implicit formatA: Format[A], formatB: Format[B]): Either[Result, B] = {
    val body = request.body
    val bodyAsJsValue = Json.toJson(body)
    if (!isTampered(bodyAsJsValue)) {
      bodyAsJsValue.validate[B].fold(
        valid   = res => Right(res),
        invalid = err => Left(BadRequest(err.toString))
      )
    } else {
      Left(BadRequest) // Request Tampered
    }
  }

  def getUser(userId: String) = Authenticate { token =>
    Action(parse.json) { request =>
      Validate(request).fold(
        badReq => badReq,
        user   =>
          // continue...
          Ok("")
      )
    }
  }
}