scalaplayframeworkdeadbolt-2

Scala Play 2.5 Form bindFromRequest: Cannot find any HTTP Request here?


I have one controller action implemented like this:

def doChangePassword = deadbolt.Restrict(List(Array(Application.USER_ROLE_KEY)))() 
{ request => // <<<<<<<<<<<< here is the request 
  Future {
    val context = JavaHelpers.createJavaContext(request)
    com.feth.play.module.pa.controllers.AuthenticateBase.noCache(context.response())

    val filledForm = Account.PasswordChangeForm.bindFromRequest
    // compilation error here, it can't see the request ^^^^^^^

    if (filledForm.hasErrors) {
      // User did not select whether to link or not link
      BadRequest(views.html.account.password_change(userService, filledForm))
    } else {
      val Some(user: UserRow) = userService.getUser(context.session)
      val newPassword = filledForm.get.password
      userService.changePassword(user, new MyUsernamePasswordAuthUser(newPassword), true)
      Redirect(routes.Application.profile).flashing(
        Application.FLASH_MESSAGE_KEY -> messagesApi.preferred(request)("playauthenticate.change_password.success")
      )
    }
  }
}

the implementation above leads to the compilation error:

[error] /home/bravegag/code/play-authenticate-usage-scala/app/controllers/Account.scala:74: Cannot find any HTTP Request here
[error]         val filledForm =  Account.PasswordChangeForm.bindFromRequest
[error]                                                     ^
[error] one error found

However, if I change line 2 from:

{ request => // <<<<<<<<<<<< here is the request 

to

{ implicit request => // <<<<<<<<<<<< here is the request 

then it compiles ... but why?


Solution

  • What you are looking for are Implicit Parameters. In short:

    Implicit Parameters can be passed just like regular or explicit parameters. In case you do not provide an implicit parameter explicitly, then the compiler will try to pass one for you. Implicits can come from various places. From the FAQ Where does Scala look for implicits?:

    1. First look in current scope
    1. Now look at associated types in

    Implicits under number 1. take precedence over those under number 2.

    By marking request as implicit in your example, you are declaring an "Implicit defined in current scope". You need to have an implicit request in place because bindFormRequest "asks" you to pass one. See its signature:

    bindFromRequest()(implicit request: Request[_]): Form[T]
    

    Now that you have an implicit request in scope, the compiler will automatically pass it to bindFromRequest.

    As I mentioned in the beginning, you could also pass request explicitly:

    val filledForm = Account.PasswordChangeForm.bindFromRequest()(request)
    

    In the latter case there is no need to declare request as implicit as you are obviously passing request explicitly. Both variants are equal. It's up to you which one you prefer.