Scala 如何以字节数组的形式访问请求体

Scala 如何以字节数组的形式访问请求体,scala,playframework,playframework-2.2,Scala,Playframework,Playframework 2.2,只要在定义操作时使用适当的正文解析器,访问请求正文的字节数组就很简单,如request.body.asRaw… 然而,我现在正在为HMAC安全操作构建一个ActionBuilder,在这里访问身体是不可避免的。问题在于ActionBuilders的定义在请求类型方面是通用的,因此在主体解析器方面也是通用的: def invokeBlock[A](request: Request[A], block: HmacRequest[A] => Future[SimpleResult]) 由于A没

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

然而,我现在正在为HMAC安全操作构建一个
ActionBuilder
,在这里访问身体是不可避免的。问题在于ActionBuilders的定义在请求类型方面是通用的,因此在主体解析器方面也是通用的:

def invokeBlock[A](request: Request[A], block: HmacRequest[A] => Future[SimpleResult])
由于
A
没有任何类型约束,因此似乎没有任何方法可以从
请求[\u]
访问请求正文

在我的具体案例中,可以做如下工作:

request.body.asInstanceOf[AnyContentAsJson].json.toString()...
但这对我来说不是一个可接受的解决方案

我还尝试定义一个自定义主体解析器并将其应用于
请求[\u]
,但结果是空的

如何访问
请求的主体[\u]
(一个字节数组表示就足够了)



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

当主体解析器成功解析请求主体时,请求类只有一个主体字段,这将导致请求[a]的实例正在创建。通常情况下,将原始字节和实例放在一起并不有趣,因为这会使每个请求占用双倍的内存

主体解析器可以继续使用或提前返回它接收的每个字节块。 也许您可以将Hmac验证内容实现为包装体解析器

对于每个输入块(数组[Byte]),您将使用包装迭代器/枚举器收集字节。当输入结束时,您触发这些字节上的hmac签名计算/验证,如果无效,您可以返回BADDREQUEST,或者将整个正文推送到实际正文解析器。

我们解决这个问题的方法(在游戏2.3中)是构造一个正文解析器,并行运行两个正文解析器。使用它,您可以运行BodyParsers.parse.raw或除主程序之外的任何程序。将原始解析器与验证(此处未显示)结合起来,生成一个左[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))
        }
}
导入scala.concurrent.ExecutionContext
导入play.api.libs.concurrent.Execution.defaultContext
导入play.api.libs.iteratee.enumerate
导入play.api.libs.iteratee.iteratee
导入play.api.mvc.BodyParser
导入play.api.mvc.RequestHeader
导入play.api.mvc.Result
/**
*并行执行任意两个提供的BodyParser的BodyParser。
*
*结果按以下方式组合:
*如果任何包装的解析器的迭代者遇到错误,这就是结果。
*否则,如果第一个解析器的Iteratee最终生成一个Left,则将其用作结果。
*否则,如果第二个解析器的Iteratee生成一个Left,则将其用作结果。
*否则,两个正确的结果组合在一个正确的[(a,B)]。
*
*这可以用来提供请求的主体,例如作为RawBuffer和json解析
*自定义模型类,或者通过HMAC模块为主体提供解析信息。
*
*@作者Jürgen Strobel
*/
最后一个案例类DualBodyParser[+A,+B](
a:BodyParser[a],
b:BodyParser[b]
)(
隐式ec:ExecutionContext=defaultContext
)
扩展BodyParser[(A,B)]
{
def apply(v1:RequestHeader):迭代对象[数组[字节],或者[结果,(A,B)]]=
枚举zipWith(a(v1),b(v1)){
案例(左(va),)=>左(va)
大小写(_,左(vb))=>左(vb)
案例(右(va),右(vb))=>右(va,vb))
}
}
我们还创建了ActionBuilder的一个自定义变体,该变体使用DualBodyParser和我们自己的验证逻辑包装任何用户提供的BodyParser,并仅在验证成功后将第二个BodyParser的结果再次拼接到用户提供的代码块。注意ActionBuilder上的这个变体复制并粘贴了大部分原始ActionBuilder代码,但是使用了一个参数来注入我们的身份验证逻辑,因此它是一个类而不是一个对象。有不同的方法来解决这个问题。在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)
}
/**
*FooAuthenticatedAction在调用其操作块之前执行Foo身份验证。
*
*这几乎完全复制了ActionBuilder和Action。
*它拼接了一个解析器。tolerantText BodyParser在中使用
*正文文本,然后继续调用提供的块,并返回
*提供的主体解析器(如果有)。
*
*@param fooheloper配置为处理传入请求的fooheloper。
*
*@作者Jürgen Strobel
*/
案例类FooAuthenticatedAction(fooHelper:fooHelper)扩展了ActionFunction[Request,Request]{
自我=>
final def apply[A](bodyParser:bodyParser[A])(block:Request[A]=>Result):操作[(字符串,A)]=
异步(bodyParser){req:Request[A]=>
Future.successful(块(请求))
}
最终定义应用(块:请求[AnyContent]=>结果):操作[(字符串,AnyContent)]=
应用(BodyParsers.parse.anyContent)(块)
最终定义应用(块:=>结果):操作[(字符串,任意内容)]=
应用(=>block)
final def async(block:=>Future[Result]):操作[(字符串,AnyContent)]=
异步(=>block)
final def async(block:Request[AnyContent]=>Future[Result]):操作[(字符串,AnyContent)]=
异步(BodyParsers.parse.anyContent)(块)
最终def异步[A](正文)