gpt4 book ai didi

scala - 如何以字节数组的形式访问 Request[_] 的主体

转载 作者:行者123 更新时间:2023-12-04 08:52:31 26 4
gpt4 key购买 nike

访问请求正文的字节数组很简单,只要在定义 Action 时使用适当的正文解析器,例如 request.body.asRaw... .

但是,我正在构建 ActionBuilder对于现在的 HMAC 安全操作,对 body 的访问是不可避免的。问题是 ActionBuilders 的定义在请求类型方面是通用的,因此对于正文解析器也是:

def invokeBlock[A](request: Request[A], block: HmacRequest[A] => Future[SimpleResult])

A没有任何类型限制,似乎没有任何方法可以从 Request[_] 访问请求正文.

在我的具体情况下,它可以执行以下操作:
request.body.asInstanceOf[AnyContentAsJson].json.toString()...

但这对我来说不是一个可接受的解决方案。

我还尝试定义自定义正文解析器并将其应用于 Request[_] ,但结果却是空的。

如何访问 Request[_] 的正文(一个字节数组表示就足够了)?

更新:如果我可以访问 ActionBuilder 中的请求正文,这也是一个可接受的解决方案。 ,例如通过将整个处理包装在另一个执行自定义解析的操作中。但我不明白这将如何工作......该解决方案应该是可重用的,因为可以将任意用户定义的操作与 HMAC 功能一起使用,而不会干扰任何用户逻辑。

最佳答案

我们解决这个问题的方法(在 Play 2.3 中)是构建一个 BodyParser,它并行运行 2 个 BodyParsers。使用它,您可以运行 BodyParsers.parse.raw 或除主程序之外的任何其他内容。将原始解析器与验证相结合(此处未显示),并生成一个 Left[Result] 以及您喜欢的任何错误消息和状态,以实现您想要的结果。

import scala.concurrent.ExecutionContext    

import play.api.libs.concurrent.Execution.defaultContext
import play.api.libs.iteratee.Enumeratee
import play.api.libs.iteratee.Iteratee
import play.api.mvc.BodyParser
import play.api.mvc.RequestHeader
import play.api.mvc.Result

/**
* A BodyParser which executes any two provided BodyParsers in parallel.
*
* The results are combined in the following way:
* If any wrapped parser's Iteratee encounters an Error, that's the result.
* Else if the first parser's Iteratee finally yields a Left, this is used as the result.
* Else if the second parser's Iteratee yields a Left, this is used as the result.
* Else both Right results are combined in a Right[(A, B)].
*
* This can be used to e.g. provide the request's body both as a RawBuffer and a json-parsed
* custom model class, or to feed the body through a HMAC module in addition to parsing it.
*
* @author Jürgen Strobel <juergen@strobel.info>
*/
final case class DualBodyParser[+A, +B](
a: BodyParser[A],
b: BodyParser[B]
)(
implicit ec: ExecutionContext = defaultContext
)
extends BodyParser[(A, B)]
{
def apply(v1: RequestHeader): Iteratee[Array[Byte], Either[Result, (A, B)]] =
Enumeratee.zipWith(a(v1), b(v1)) {
case (Left(va), _) => Left(va)
case (_, Left(vb)) => Left(vb)
case (Right(va), Right(vb)) => Right((va, vb))
}
}

我们还创建了一个自定义的 ActionBuilder 变体,它使用 DualBodyParser 和我们自己的验证逻辑包装任何用户提供的 BodyParser,并且只有在身份验证成功后才将第二个的结果再次拼接到用户提供的代码块中。请注意,ActionBuilder 的这种变体复制并粘贴了大部分原始 ActionBuilder 代码,但需要一个参数来注入(inject)我们的身份验证逻辑,因此它是一个类而不是对象。将有不同的方法来解决这个问题。在 BodyParser 中封装完整的身份验证逻辑在 TODO 列表中,并且可能更容易和更好地组合。
/**
* An FooAuthenticatedAction does Foo authentication before invoking its action block.
*
* This replicates ActionBuilder and Action almost fully.
* It splices a parser.tolerantText BodyParser in, does Foo authentication with the
* body text, and then continues to call the provided block with the result of the
* provided body parser (if any).
*
* @param fooHelper An FooHelper configured to handle the incoming requests.
*
* @author Jürgen Strobel <juergen@strobel.info>
*/
case class FooAuthenticatedAction(fooHelper: FooHelper) extends ActionFunction[Request, Request] {
self =>

final def apply[A](bodyParser: BodyParser[A])(block: Request[A] => Result): Action[(String, A)] =
async(bodyParser) { req: Request[A] =>
Future.successful(block(req))
}

final def apply(block: Request[AnyContent] => Result): Action[(String, AnyContent)] =
apply(BodyParsers.parse.anyContent)(block)

final def apply(block: => Result): Action[(String, AnyContent)] =
apply(_ => block)

final def async(block: => Future[Result]): Action[(String, AnyContent)] =
async(_ => block)

final def async(block: Request[AnyContent] => Future[Result]): Action[(String, AnyContent)] =
async(BodyParsers.parse.anyContent)(block)

final def async[A](bodyParser: BodyParser[A])(block: Request[A] => Future[Result]): Action[(String, A)] =
composeAction(
new Action[(String, A)] {
def parser = DualBodyParser(parse.tolerantText, composeParser(bodyParser))
def apply(request: Request[(String, A)]) = try {
fooHelper.authenticate(request map (_._1)) match {
case Left(error) => failUnauthorized(error)
case Right(_key) => invokeBlock(request map (_._2), block)
}
} catch {
case e: NotImplementedError => throw new RuntimeException(e)
case e: LinkageError => throw new RuntimeException(e)
}
override def executionContext = self.executionContext
}
)

/**
* This produces the Result if authentication fails.
*/
def failUnauthorized(error: String): Future[Result] =
Future.successful( Unauthorized(error) )

protected def composeParser[A](bodyParser: BodyParser[A]): BodyParser[A] = bodyParser

protected def composeAction[A](action: Action[A]): Action[A] = action

// we don't use/support this atm
/**
override def andThen[Q[_]](other: ActionFunction[R, Q]): ActionBuilder[Q] = new ActionBuilder[Q]
**/

def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) =
block(request)
}

关于scala - 如何以字节数组的形式访问 Request[_] 的主体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19888717/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com