2016-04-14 4 views
0

В проекте Play 2.4 я создал действие, которое выполняет три действия.Play 2.4 ActionBuilder/ActionFunction, BodyParsers и JSON

  1. проверьте, что имеется конкретный заголовок. Если нет, возвращает ошибку HTTP.
  2. использовать значение заголовка для аутентификации пользователя (функция аутентификации будет передана этому действию). Если ошибка auth возвращает ошибку HTTP
  3. разобрать тело 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 и мою желаемую композицию. Мой вопрос о том, как улучшить мою композицию.

Благодаря
Жюль

+0

Возможный дубликат [Играть: Как реализовать композицию действия] (http://stackoverflow.com/questions/25105558/play-how-to-implement-action-composition) – cchantep

+0

Нет, потому что этот пост объясняет простой случай и не отвечайте на мои вопросы. –

+0

Он сочиняет действия, используя «BodyParser», поэтому понятия одинаковы, решения довольно похожи. – cchantep

ответ

0

Вместо того, чтобы новый ActionBuilder для JSONRequest, я бы просто использовать AuthentificationActionActionBuilder и передать его JSON BodyParser:

AuthentificationAction(parse.json) { 
    request => // Note that the request has type Request[JsValue] 
    doStuffWithJson(request.body) 
} 

Любое действие, используя этот строитель, будет получите Request[JsValue], а не Request[AnyContent].

+0

Я не считаю ваше предложение очень полезным. Код будет менее понятным, если я сделаю это –

Смежные вопросы