2012-09-27 23 views
6

Estoy intentando autenticar usuarios mediante el servicio de autenticación remota. método de ayuda que he escrito para enviar el mensaje al servicio y en espera de resultado:Limitación de la función Play 2

def authenticateAwait(email:  String, 
         password: String 
        ): Either[String, Option[User]] = { 
    try { 
    val future = authenticate(email, password) 
    Right(Await.result(future, timeout.duration)) 
    } catch { 
    case _ ⇒ Left("Unable to connect to authentication server") 
    } 
} 

Devuelve Left[String] con una descripción del error si el mensaje no puede ser enviado, o no hay respuesta. Si se recibe respuesta de servicio, devuelve Right[Option[User]]. El servicio responde con Option[User] dependiendo del resultado de la autenticación.

para realizar la autenticación real que he creado formulario con un par de validadores, aquí está:

val loginForm = Form(
 tuple(
    "email"    → email, 
    "password" → nonEmptyText 
) verifying ("Invalid email or password", result => result match { 
    case (email, password) ⇒ 
     User.authenticateAwait(email, password) match { 
     case Left(_) ⇒ true 
     case Right(optUser) ⇒ optUser.isDefined 
     } 
    }) verifying ("Unable to connect to authentication server", result => result match { 
    case (email, password) ⇒ 
     User.authenticateAwait(email, password) match { 
     case Left(_) ⇒ false 
     case Right(optUser) ⇒ true 
     } 
    }) 
) 

Una cosa que me preocupa de este código, se llama authenticateAwait dos veces. Significa que se enviarán exactamente dos mensajes por validación individual. Lo que realmente necesito es llamar al authenticateAwait una vez, almacenar el resultado y realizar varias validaciones en él. Parece que no hay una solución simple.

Para realizar la autenticación, acceder a los campos de formulario requeridos, significa que el formulario debe ser vinculado y luego validado, pero no hay forma de adjuntar errores al formulario existente (¿estoy equivocado?).

Los errores se pueden adjuntar al formulario solo durante su creación, por lo tanto, debo realizar la autenticación en los validadores, pero se produce el problema antes mencionado.

La solución temporal con la que vine es definir un método y un var dentro de él.

def loginForm = { 
    var authResponse: Either[String, Option[commons.User]] = null 

    Form(
    tuple(
     "email" → email, 
     "password" → nonEmptyText 
    ) verifying ("Invalid email or password", result ⇒ result match { 
     case (email, password) ⇒ 
     authResponse = User.authenticateAwait(email, password) 
     authResponse match { 
      case Left(_) ⇒ true 
      case Right(optUser) ⇒ optUser.isDefined 
     } 
    }) verifying ("Unable to connect to authentication server", result ⇒ result match { 
     case (email, password) ⇒ 
     authResponse match { 
      case Left(_) ⇒ false 
      case Right(optUser) ⇒ true 
     } 
    }) 
) 
} 

Esto es claramente un truco. ¿Hay mejores soluciones?

Actualización: En mi opinión, la forma solo debe sanitizar la entrada, pero la autenticación debe realizarse más adelante fuera del formulario. El problema es que los errores se envían a la vista como parte del Form y es imposible adjuntar errores al formulario existente. No hay una forma simple de crear una nueva forma con los errores también.

+1

se llama AJAX; úselo y no creará bloques de código descomunales tratando de resolver un problema que no existe (sugerencia: no es necesario crear un nuevo formulario) – virtualeyes

Respuesta

3

Lo que debes entender es que la Forma es inmutable. Pero hay un método de utilidad fácil de usar para construir un nuevo formulario con los errores añadidos:

loginForm.copy(errors = Seq(FormError("email", "Already registered"))) 
0

seguro, la combinación de autenticación con validación solo hace que una operación simple sea compleja. A continuación no se ha probado, pero esta es la dirección en la que entraría, las proyecciones correctas filtradas a través de una para la comprensión.

// point of validation is to sanitize inputs last I checked 
val form = Form(tuple("email"→ email, "password"→ nonEmptyText) 
val res = for{ 
    case(e,p) <- form.bindFromRequest.toRight("Invalid email or password") 
    success <- User.authenticateAwait(e,p).right 
} yield success 
res fold(Conflict(Left(_)), u=> Ok(Right(u))) 
+0

Las muestras de reproducción contienen autenticación dentro del bloque de verificación de formularios, por lo tanto los verificadores son una forma ir. Siguiendo su solución, si la autenticación falla, ¿cómo debo devolver el formulario con un error a la plantilla? – lambdas

+0

ya lo está haciendo, la izquierda contiene un error de validación de formulario o un error de conexión; La derecha contiene al usuario aparentemente ... de todos modos, solo envuélvelo en un Resultado, Conflicto (Izquierda (_)), u => Ok (Derecha (u)) – virtualeyes

+0

también, supongo que dado que está tomando la ruta del futuro , ese rendimiento es crítico; como tal, en una aplicación web estándar.ajax sería el camino a seguir, IMO: de esa manera no hay necesidad de volver a mostrar el formulario por error, el usuario nunca va a ninguna parte, solo tiene que lidiar con la condición de error y mostrarlo, o devolver el éxito y manejarlo en consecuencia. – virtualeyes

Cuestiones relacionadas