2013-02-16 7 views
30

मैं प्ले की एसिंक शक्तियों को समझने के लिए एक वास्तविक धक्का दे रहा हूं लेकिन उन स्थानों के संबंध में बहुत सारे संघर्ष ढूंढ रहा हूं जहां एसिंक इनवोकेशन फिट बैठता है और स्थान जहां ढांचे के उपयोग के खिलाफ षड्यंत्र लगता है।क्या मैं प्ले फ्रेमवर्क 2.x (स्कैला) में एसिंक फॉर्म सत्यापन कर सकता हूं?

उदाहरण मैंने फॉर्म सत्यापन से संबंधित है। प्ले से परिभाषित करने की तदर्थ बाधाओं के लिए अनुमति देता है - डॉक्स से देखें:

val loginForm = Form(
    tuple(
    "email" -> email, 
    "password" -> text 
) verifying("Invalid user name or password", fields => fields match { 
     case (e, p) => User.authenticate(e,p).isDefined 
    }) 
) 

अच्छा और साफ। हालांकि, अगर मैं पूरी तरह से एसिंक डेटा एक्सेस लेयर (उदाहरण के लिए रीएक्टिवमोन्गो) का उपयोग कर रहा हूं, तो User.authenticate(...) पर ऐसी कॉल Future लौटाएगी और मैं इस प्रकार अंधेरे में हूं कि मैं कैसे निर्मित फॉर्म बाध्यकारी की शक्ति का उपयोग कर सकता हूं विशेषताएं और async उपकरण।

एसिंक दृष्टिकोण को प्रचारित करना अच्छा और अच्छा है, लेकिन मैं निराश हो रहा हूं कि ढांचे के कुछ हिस्सों में इसके साथ बहुत अच्छा खेल नहीं है। अगर सत्यापन को समकालिक रूप से किया जाना है, तो यह एसिंक दृष्टिकोण के बिंदु को हराने के लिए प्रतीत होता है। Action संरचना का उपयोग करते समय मुझे एक ही समस्या आई है - उदा। एक सुरक्षा संबंधित Action जो ReactiveMongo को कॉल करेगी।

क्या कोई मेरी रोशनी कम होने पर कोई प्रकाश डाल सकता है?

उत्तर

9

हां, Play में सत्यापन सिंक्रनाइज़ रूप से डिज़ाइन किया गया है। मुझे लगता है कि ऐसा इसलिए माना जाता है कि अधिकांश समय फॉर्म सत्यापन में कोई I/O नहीं है: फ़ील्ड मानों को आकार, लंबाई, रेगेक्सपी के विरुद्ध मिलान करने के लिए चेक किया गया है।

प्रमाणीकरण play.api.data.validation.Constraint पर बनाया गया है जो मान्य मान से स्टोर फ़ंक्शन ValidationResult (या तो Valid या Invalid पर, यहां Future डालने की कोई जगह नहीं है)।

/** 
* A form constraint. 
* 
* @tparam T type of values handled by this constraint 
* @param name the constraint name, to be displayed to final user 
* @param args the message arguments, to format the constraint name 
* @param f the validation function 
*/ 
case class Constraint[-T](name: Option[String], args: Seq[Any])(f: (T => ValidationResult)) { 

    /** 
    * Run the constraint validation. 
    * 
    * @param t the value to validate 
    * @return the validation result 
    */ 
    def apply(t: T): ValidationResult = f(t) 
} 

verifying सिर्फ उपयोगकर्ता परिभाषित समारोह के साथ एक और बाधा कहते हैं।

तो मुझे लगता है कि प्ले में डेटा बाध्यकारी सिर्फ सत्यापन के दौरान I/O करने के लिए डिज़ाइन नहीं किया गया है। इसे अतुल्यकालिक बनाना इसे अधिक जटिल और उपयोग करने में कठिन बना देगा, इसलिए यह आसान रखा गया। Future एस में लिपटे डेटा पर काम करने के लिए फ्रेमवर्क में कोड के प्रत्येक टुकड़े को ओवरकिल करना है।

यदि आपको ReactiveMongo के साथ सत्यापन का उपयोग करने की आवश्यकता है, तो आप Await.result का उपयोग कर सकते हैं। ReactiveMongo हर जगह फ्यूचर्स लौटाता है, और आप verifying फ़ंक्शन के अंदर परिणाम प्राप्त करने के लिए इन फ़्यूचर्स को पूरा होने तक अवरुद्ध कर सकते हैं। हाँ, यह एक धागा बर्बाद कर देगा जबकि MongoDB क्वेरी चलती है।

object Application extends Controller { 
    def checkUser(e:String, p:String):Boolean = { 
    // ... construct cursor, etc 
    val result = cursor.toList().map(_.length != 0) 

    Await.result(result, 5 seconds) 
    } 

    val loginForm = Form(
    tuple(
     "email" -> email, 
     "password" -> text 
    ) verifying("Invalid user name or password", fields => fields match { 
     case (e, p) => checkUser(e, p) 
    }) 
) 

    def index = Action { implicit request => 
    if (loginForm.bindFromRequest.hasErrors) 
     Ok("Invalid user name") 
    else 
     Ok("Login ok") 
    } 
} 

हो सकता है कि वहाँ रास्ता continuations का उपयोग करके धागा बर्बाद नहीं करने के लिए, इसे करने की कोशिश नहीं।

मुझे लगता है कि प्ले मेलिंग सूची में इस पर चर्चा करना अच्छा है, शायद कई लोग प्ले डेटा बाइंडिंग में एसिंक्रोनस I/O करना चाहते हैं (उदाहरण के लिए, डेटाबेस के विरुद्ध मूल्यों की जांच के लिए), इसलिए कोई इसे भविष्य के संस्करणों के लिए कार्यान्वित कर सकता है प्ले।

+0

मैं कैसे मान्यता संदेश dynamicly निर्धारित कर सकते हैं:

से निकाले गए? उदाहरण के लिए संदेश "अवैध उपयोगकर्ता नाम या पासवर्ड" या "सेवा अनुपलब्ध" हो सकता है। और दूसरा सवाल यह है कि क्या मैं उपयोगकर्ता ऑब्जेक्ट को डुप्लिकेट ऑथ-अनुरोध के बिना क्रिया में प्राप्त कर सकता हूं? – Artem

6

मैं भी इसके साथ संघर्ष कर रहा हूं। यथार्थवादी अनुप्रयोगों में आमतौर पर कुछ प्रकार के उपयोगकर्ता खाते और प्रमाणीकरण होते हैं।द्वारा

def authenticate = Action { implicit request => 
    Async { 
    val (username, password) = loginForm.bindFromRequest.get 
    User.authenticate(username, password).map { user => 
     user match { 
     case Some(u: User) => Redirect(routes.Application.index).withSession("username" -> username) 
     case None => Redirect(routes.Application.login).withNewSession.flashing("Login Failed" -> "Invalid username or password.") 
     } 
    } 
    } 
} 
3

फार्म सत्यापन क्षेत्रों में वाक्यात्मक सत्यापन का मतलब है, एक के बजाय: धागा अवरुद्ध करने का एक वैकल्पिक रूप से बाहर मानकों मिलता है और नियंत्रक विधि अपने आप में प्रमाणीकरण कॉल, कुछ इस तरह संभाल करने होगा एक। यदि कोई दायर सत्यापन पास नहीं करता है तो इसे चिह्नित किया जा सकता है (उदाहरण के लिए संदेश के साथ लाल बार)।

प्रमाणीकरण कार्रवाई के शरीर में रखा जाना चाहिए, जो एक असिंक ब्लॉक में हो सकता है। यह bindFromRequest कॉल के बाद होना चाहिए, इसलिए वहाँ इतने के बाद प्रत्येक क्षेत्र नहीं खाली, आदि

async कॉल का परिणाम के आधार पर है (उदाहरण के लिए। ReactiveMongo कॉल) सत्यापन के बाद मुझे चाहिए, कार्रवाई के परिणाम या तो BadRequest या ठीक हो सकता है।

BadRequest और OK दोनों के साथ प्रमाणीकरण विफल होने पर त्रुटि संदेश के साथ फ़ॉर्म को फिर से चला सकते हैं। ये सहायक केवल प्रतिक्रिया के HTTP स्थिति कोड को प्रतिक्रिया शरीर को स्वतंत्र रूप से निर्दिष्ट करते हैं।

यह play.api.mvc.Security.Authenticated (या एक समान, अनुकूलित क्रिया कंपोजिटर) के साथ प्रमाणीकरण करने का एक शानदार समाधान होगा, और फ्लैश स्कॉप्ड संदेशों का उपयोग करें। इस प्रकार उपयोगकर्ता को हमेशा लॉगिन पृष्ठ पर रीडायरेक्ट किया जाएगा यदि उसे प्रमाणित नहीं किया गया है, लेकिन यदि वह गलत प्रमाण-पत्र के साथ लॉगिन फॉर्म सबमिट करती है तो रीडायरेक्ट के अलावा त्रुटि संदेश दिखाया जाएगा।

कृपया अपने प्ले इंस्टॉलेशन के जेनटास्क उदाहरण पर एक नज़र डालें।

मैं वास्तविक प्रमाणीकरण फ़ॉर्म सत्यापन से बाहर अपनी कार्रवाई में बजाय ले जाने और इसे और केवल के सत्यापन के लिए सत्यापन का उपयोग करेंगे:

1

ही प्रश्न का उत्तर देते जोहान Andren साथ खेलने मेलिंग सूची में asked था आवश्यक फ़ील्ड आदि इस तरह कुछ:

val loginForm = Form(
    tuple(
    "email" -> email, 
    "password" -> text 
) 
) 

def authenticate = Action { implicit request => 
    loginForm.bindFromRequest.fold(
    formWithErrors => BadRequest(html.login(formWithErrors)), 
    auth => Async { 
     User.authenticate(auth._1, auth._2).map { maybeUser => 
     maybeUser.map(user => gotoLoginSucceeded(user.get.id)) 
     .getOrElse(... failed login page ...) 
     } 
    } 
) 
} 
0

मैं द गार्डियन के जीएच रेपो कि वे किस तरह एक अतुल्यकालिक तरीके से इस स्थिति को संभालने, जबकि अभी भी खेल से प्रपत्र त्रुटि सहायकों के समर्थन होने पर देखा है। एक त्वरित रूप से, ऐसा लगता है कि वे एक एन्क्रिप्टेड कुकी में फ़ॉर्म त्रुटियों को संग्रहीत कर रहे हैं ताकि अगली बार जब उपयोगकर्ता लॉगिन पृष्ठ पर जाता है तो उन त्रुटियों को उपयोगकर्ता को वापस प्रदर्शित करने के लिए। https://github.com/guardian/facia-tool/blob/9ec455804edbd104861117d477de9a0565776767/identity/app/controllers/ReauthenticationController.scala

def processForm = authenticatedActions.authActionWithUser.async { implicit request => 
    val idRequest = idRequestParser(request) 
    val boundForm = formWithConstraints.bindFromRequest 
    val verifiedReturnUrlAsOpt = returnUrlVerifier.getVerifiedReturnUrl(request) 

    def onError(formWithErrors: Form[String]): Future[Result] = { 
    logger.info("Invalid reauthentication form submission") 
    Future.successful { 
     redirectToSigninPage(formWithErrors, verifiedReturnUrlAsOpt) 
    } 
    } 

    def onSuccess(password: String): Future[Result] = { 
     logger.trace("reauthenticating with ID API") 
     val persistent = request.user.auth match { 
     case ScGuU(_, v) => v.isPersistent 
     case _ => false 
     } 
     val auth = EmailPassword(request.user.primaryEmailAddress, password, idRequest.clientIp) 
     val authResponse = api.authBrowser(auth, idRequest.trackingData, Some(persistent)) 

     signInService.getCookies(authResponse, persistent) map { 
     case Left(errors) => 
      logger.error(errors.toString()) 
      logger.info(s"Reauthentication failed for user, ${errors.toString()}") 
      val formWithErrors = errors.foldLeft(boundForm) { (formFold, error) => 
      val errorMessage = 
       if ("Invalid email or password" == error.message) Messages("error.login") 
       else error.description 
      formFold.withError(error.context.getOrElse(""), errorMessage) 
      } 

      redirectToSigninPage(formWithErrors, verifiedReturnUrlAsOpt) 

     case Right(responseCookies) => 
      logger.trace("Logging user in") 
      SeeOther(verifiedReturnUrlAsOpt.getOrElse(returnUrlVerifier.defaultReturnUrl)) 
      .withCookies(responseCookies:_*) 
     } 
    } 

    boundForm.fold[Future[Result]](onError, onSuccess) 
} 

def redirectToSigninPage(formWithErrors: Form[String], returnUrl: Option[String]): Result = { 
    NoCache(SeeOther(routes.ReauthenticationController.renderForm(returnUrl).url).flashing(clearPassword(formWithErrors).toFlash)) 
} 
+0

एन्क्रिप्शन सामान "toFlash" अंतर्निहित विधि में जाता है जो उनकी फ़ाइल implicits में पाया जा सकता है। Forms.scala – dzv3

संबंधित मुद्दे