2015-04-30 3 views
0

Предположим, у меня есть эта модель, в которой я определил адрес IPAddress с индивидуальным кодом класса с определенными форматами JSON. Этот класс содержит строковое представление IPv4 и содержит проверку в конструкторе, которая вызывает исключение IllegalArgumentException, если строка ввода недопустима.Play2/Scala Пользовательская проверка поля JSON

case class Node(id: UUID, name: String, ip: IPAddress) 

case class IPAddress(s: String) { 
    val rx = """^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$""".r 
    require(rx.pattern.matcher(s).matches()) 

    val ip = s 
    override def toString = ip 
} 


object JsonNodeFormat { 
    import play.api.libs.json.Json 

    implicit val midWrite: Writes[FMMid] = Writes { 
    (mid: FMMid) => JsString(mid.toString) 
    } 
    implicit val midRead: Reads[FMMid] = JsPath.read[String].map(FMMid(_)) 

    implicit val NodeFormat = Json.format[Node] 
} 

Тогда у меня есть контроллер с действием, которое создает новый узел и запись в базе данных (в моем случае я использую ReactiveMongo, но это не имеет значения)

class Nodes extends Controller with MongoController { 
    def collection: JSONCollection = db.collection[JSONCollection]("nodes") 

    import models._ 
    import models.JsonNodeFormat._ 

    def createTest = Action.async(parse.json) { 
    request => 
     request.body.validate[Node].map { 
     node => 
      collection.insert(node).map { 
      lastError => 
       Created(s"Node Created") 
      } 
     }.getOrElse(Future.successful(BadRequest("invalid json"))) 
    } 
} 

Если марка запрос с действительным json

{ 
    "id": "0879d4be-78bb-4cc0-810b965b", 
    "ip": "192.168.0.10", 
    "name": "node1" 
} 

все работает нормально. Объект правильно добавлен в db. Я бы хотел, чтобы контроллер возвращал ответ BadRequest при передаче недопустимого IP-адреса. Наоборот, если я прохожу JSon с некорректным IP адрес

{ 
    "id": "0879d4be-78bb-4cc0-810b965b", 
    "ip": "192.168.0.foo", 
    "name": "node1" 
} 

это вызывает внутреннюю ошибку сервера со всеми стека, напечатанной на консоли (потому что никто не ловит исключение конструктора). Я хотел бы, чтобы при передаче недопустимого IPAddress функция request.body.validate завершилась неудачно, и выполнение попало в оператор getOrElse.

Также обратите внимание, что передача недопустимого UUID не генерирует ошибку, а ответ BadRequest.

Что отсутствует в классе IPAddress?

+0

Вы думаете о возврате 'Try [String]' вместо String? Вы можете обернуть проверку ip, чтобы вы могли реагировать, если у вас есть проблема проверки. –

+0

Да, это абсолютно решение. Но я хотел потребовать к body.validate всю работу, так что мне не понадобится еще один блок кода и, следовательно, дополнительный уровень вложенности. Мне просто интересно, потому что все работает так, как ожидалось, с UUID. – tano

+0

Play дает вам возможность решить, как ответить на конкретное исключение. Вы можете переопределить определение функции onError внутри объекта Global. Там вы можете получить исключение, и вы сможете вернуть код BadRequest. –

ответ

1

В конце концов я нашел решение. Я обнаружил, что между другими помощниками проверки Reads есть один, который называется pattern, который соответствует строке с регулярным выражением. Поэтому я изменил свой Json Reads как

implicit val midRead: Reads[FMMid] = JsPath.read[String](pattern("""^5\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$""".r)).map(FMMid(_)) 

и все началось, как я и ожидал.

P.S. все еще не могут понять, как работает UUID без какого-либо пользовательского валидатора.

+0

Play предоставляет 'Reads [UUID]', поэтому вам не нужно создавать свои собственные. – Ryan

Смежные вопросы