В проекте Play 2.4 я создал действие, которое выполняет три действия.Play 2.4 ActionBuilder/ActionFunction, BodyParsers и JSON
- проверьте, что имеется конкретный заголовок. Если нет, возвращает ошибку HTTP.
- использовать значение заголовка для аутентификации пользователя (функция аутентификации будет передана этому действию). Если ошибка auth возвращает ошибку HTTP
- разобрать тело JSON на класс case и передать его в код блока действий.
Чтобы сделать это, я использовал Play Действие композиции Механизм, объяснить на этой странице: https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition
, но более конкретно, конечный результат я хотел объясняется здесь: https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition#Putting-it-all-together
мне удалось написать это:
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])
потому что кажется, тх t Я не могу этого сделать: case class ParsedJsonRequest[T, A](parsed: T, request: Request[A])
Возможно ли это? Как вы думаете, я могу улучшить свое решение? Как ?
Мой вопрос не о том, как это сделать. Я понимаю, как это работает, и мне удалось написать мои ActionBuilders и мою желаемую композицию. Мой вопрос о том, как улучшить мою композицию.
Благодаря
Жюль
Возможный дубликат [Играть: Как реализовать композицию действия] (http://stackoverflow.com/questions/25105558/play-how-to-implement-action-composition) – cchantep
Нет, потому что этот пост объясняет простой случай и не отвечайте на мои вопросы. –
Он сочиняет действия, используя «BodyParser», поэтому понятия одинаковы, решения довольно похожи. – cchantep