2014-10-11 11 views
12

'daki Özel JSON doğrulama sınırlamaları Özel kısıtlamalar ile form doğrulaması yapmayı başardım, ancak şimdi aynı şeyi JSON verileriyle yapmak istiyorum.Play Framework 2.3 (Scala)

Özel doğrulama kurallarını JSON ayrıştırıcısına nasıl uygularım?

Örnek: İstemcinin POST isteği bir kullanıcı adı (username) içerir ve yalnızca bu parametrenin boş olmayan bir metin olduğundan emin olmak istemiyorum, aynı zamanda bu kullanıcının veritabanında da var olduğundan emin olmak istiyorum. bildiğim kadarıyla olsun, ama bu sadece username String olmasını sağlar

// In the controller... 

def postNew = Action { implicit request => 
    request.body.asJson.map { json => 
     json.validate[ExampleCaseClass] match { 
      case success: JsSuccess[ExampleCaseClass] => 
       val obj: ExampleCaseClass = success.get 
       // ...do something with obj... 
       Ok("ok") 
      case error: JsError => 
       BadRequest(JsError.toFlatJson(error)) 
     } 
    } getOrElse(BadRequest(Json.obj("msg" -> "JSON request expected"))) 
} 


// In ExampleCaseClass.scala... 

case class ExampleCaseClass(username: String, somethingElse: String) 

object ExampleCaseClass { 
    // That's what I would use for a form: 
    val userCheck: Mapping[String] = nonEmptyText.verifying(userExistsConstraint) 

    implicit val exampleReads: Reads[ExampleCaseClass] = (
     (JsPath \ "username").read[String] and 
     (JsPath \ "somethingElse").read[String] 
    )(ExampleCaseClass.apply _) 
} 

. Ek özel doğrulama kuralımı, ör. Verilen kullanıcının gerçekten var olup olmadığını kontrol etmek için? Bu mümkün mü? Sonra ben kendi hata mesajı oluşturmak zorunda ve bunu ancak kullanıcı JsError.toFlatJson(error) ediyorum çünkü

Tabi, eylem benim obj yılında case success bölümünü alıp orada ek denetimler gerçekleştiren, ancak bu çok şık görünmüyor olabilir bazı durumlar için. Saatlerce aradıktan ve denedikten sonra hiçbir örnek bulamadım.

// In the controller object... 

val userValidConstraint: Constraint[String] = Constraint("constraints.uservalid")({ username => 
    if (User.find(username).isDefined) { 
     Valid 
    } else { 
     val errors = Seq(ValidationError("User does not exist")) 
     Invalid(errors) 
    } 
}) 

val userCheck: Mapping[String] = nonEmptyText.verifying(userValidConstraint) 

val exampleForm = Form(
    mapping(
     "username" -> userCheck 
     // ...and maybe some more fields... 
    )(ExampleCaseClass.apply)(ExampleCaseClass.unapply) 
) 


// In the controller's action method... 

exampleForm.bindFromRequest.fold(
    formWithErrors => { 
     BadRequest("Example error message") 
    }, 
    formData => { 
     // do something 
     Ok("Valid!") 
    } 
) 

Ama hangi verileri JSON olarak gönderilirse:

düzenli formları için böyle bir şey kullanmak istiyorum?

+1

. Bence JSON validasyonu Scala obje arazisine girebilmek için en iyi şekilde kullanılır, ve sonra yapmanız gereken her şey oradan daha rahat olacaktır. – acjay

cevap

16

Düşünebildiğim en basit yol Reads numaralı filter yöntemini kullanır.

en biz kullanıcı ismi olup olmadığını belirlemek olacak bazı User nesne var diyelim:

object User { 
    def findByName(name: String): Option[User] = ... 
} 

Ardından olustursaydiniz senin böyle Reads:

Sizin kontrolör işlevini kullanarak basitleştirilmiş edilebilir
import play.api.libs.json._ 
import play.api.libs.functional.syntax._ 
import play.api.data.validation._ 

case class ExampleCaseClass(username: String, somethingElse: String) 

object ExampleCaseClass { 
    implicit val exampleReads: Reads[ExampleCaseClass] = (
     (JsPath \ "username").read[String].filter(ValidationError("User does not exist."))(findByName(_).isDefined) and 
     (JsPath \ "somethingElse").read[String] 
    )(ExampleCaseClass.apply _) 
} 

json BodyParser ve fold:

def postNew = Action(parse.json) { implicit request => 
    request.body.validate[ExampleCaseClass].fold(
     error => BadRequest(JsError.toFlatJson(error)), 
     obj => { 
      // Do something with the validated object.. 
     } 
    ) 
} 

Ayrıca kullanıcı varsa kontrol edecek ayrı Reads[String] oluşturabilir ve açıkça sizin Reads[ExampleCaseClass] içinde Reads[String] kullanan: Ben JSON doğrulama sonra kullanıcıların doğrulanması öneririm

val userValidate = Reads.StringReads.filter(ValidationError("User does not exist."))(findByName(_).isDefined) 

implicit val exampleReads: Reads[ExampleCaseClass] = (
    (JsPath \ "username").read[String](userValidate) and 
    (JsPath \ "somethingElse").read[String] 
)(ExampleCaseClass.apply _) 
+0

m-z şudur ki, eğer böyle bir çok doğrulayıcı kullanırsanız, bir öncekinin başarısız olması durumunda sonrakiler çalışmaya devam edecektir. Bkz., Örneğin: http://stackoverflow.com/questions/31077937/force-play-scala-json-composite-validators-to-fail-on-first-failed-validator –

+0

@FelipeAlmeida Genel olarak daha iyidir ilkinde durmak yerine hataları toplar (bu yüzden kütüphane bu şekilde oluşturulur). Bu yüzden kişisel olarak böyle bir veritabanı çağrısı yapan bir JSON doğrulayıcı kullanmamayı ve başka bir yerde ele almayı tercih etmem. Ama bu sorunun konusu değil. –

+1

Eğer findByName 'Future' veya DBIO (slick)' i döndürdüğünüzden daha fazla işe yaramazsa, bunun işe yaramayacağına inanıyorum. Oyun çerçevesinin doğrulama API'sı, Futures'ı kasıtlı olarak desteklemek için yazılmamıştır. – Ciantic