scala – 如何以字节数组的形式访问Request [_]的主体
只要在定义Action时使用适当的主体解析器(如request.body.asRaw ….),访问请求体的字节数组就很简单了.
但是,我现在正在为HMAC安全的动作构建一个ActionBuilder,在那里访问身体是不可避免的.问题是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的自定义变体,它将任何用户提供的BodyParser包装为DualBodyParser和我们自己的验证逻辑,并且只有在身份验证成功后才将第二个结果再次拼接到用户提供的代码块.注意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) } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |