rsan
rsan

Reputation: 1887

How to refactor PlayFramework Action and Async Action to avoid code duplication?

I have a method that produces an Action validating if a user token exist in the request:

def HasToken[A](p: BodyParser[A] = parse.anyContent)(
    f: String => Long => Request[A] => Result): Action[A] =
    Action(p) { implicit request =>
      request.cookies.get("XSRF-TOKEN").fold {
        invalidXSRF
      } { xsrfTokenCookie =>
        val maybeToken = request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey))
        maybeToken flatMap { token =>
          cache.get[Long](token) map { userId =>
            if (xsrfTokenCookie.value == token) {
              f(token)(userId)(request)
            } else {
              invalidToken
            }
          }
        } getOrElse noCookie
      }
    }

Then I can use this action in my controller like this:

def method = HasToken(parse.empty) { 
token => userId => implicit request => Ok("") 
}

But I started using reactive-mongo in the project and all the queries to the database return a Future. What I think is really good. In order to validate the user while using this reactive-api I had to write a new Action validation method like this:

def AsyncHasToken[A](p: BodyParser[A] = parse.anyContent)(
    f: String => Long => Request[A] => Future[Result])(implicit ec: ExecutionContext): Action[A] =
    Action.async(p) { implicit request =>
      request.cookies.get("XSRF-TOKEN").fold {
        Future(invalidXSRF)
      } { xsrfTokenCookie =>
        val maybeToken = request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey))
        maybeToken flatMap { token =>
          cache.get[Long](token) map { userId =>
            if (xsrfTokenCookie.value == token) {
              f(token)(userId)(request)
            } else {
              Future(invalidToken)
            }
          }
        } getOrElse Future(noCookie)
      }
    }

So, when I need to return a Future in my controller methods I use it like this:

def method() = AsyncHasToken(parse.empty) { 
token => userId => implicit request => Future(Ok("")) 
}

After many hours trying to refactor HasToken and AsyncHasToken I havent been able to produce satisfactory results. Is there a way to write this code more elegantly?

I think the question is more related to refactoring scala functions than Playframework, but I feel that I will be facing this pattern a lot when writing generic Actions and Async Actions thru my project.

Thanks in advance.

Upvotes: 0

Views: 87

Answers (1)

vdebergue
vdebergue

Reputation: 2404

For this I would use the Play ActionBuilder, it will allow you to keep the logic in one place and use your action with both Result and Future[Result].

cf: https://playframework.com/documentation/2.4.x/ScalaActionsComposition

Upvotes: 1

Related Questions