Я заметил, что когда я добавляю более 18 параметров в класс формы Play Framework, я получаю длинную (и непонятную для меня) ошибку компиляции.
Это задокументированное ограничение? Мне нужно принять до 29 параметров в сообщении формы. Я не решаюсь на дизайн и количество параметров, так как реализую протокол из открытого стандарта.
Я отображаю следующим образом:
val registration = Form(mapping(
"client_type" -> nonEmptyText,
"client_id" -> optional(nonEmptyText),
... up to 29 args, all optional(nonEmptyText)
){ (clientType, clientId ...) => RegistrationRequest(clientType, clientId ...) }
{ req => None })
Моя стратегия заключалась в том, чтобы сделать отображение таким образом, вместо применения/отмены применения и создания иерархии классов case. Причина в том, чтобы обойти ограничение в 22 аргумента в классах Case, которое было первым, казалось бы, произвольным ограничением, с которым я столкнулся. Работает сопоставление до 18 аргументов, после чего я получаю длинную ошибку компиляции.
Сообщение об ошибке можно найти здесь (слишком длинное, чтобы его включать): https://gist.github.com/2928297
Я ищу предложения о том, как обойти это ограничение. Я знаю, что отправлять 29 параметров в форму Post — плохой дизайн, но это все же возможно.
Взлом/Обходной путь/Решение
Хорошо, вот мой собранный обходной путь (написание этого поста заняло гораздо больше времени, чем реализация, я занимался этим примерно 30 минут)
Я написал функции, которые предварительно обрабатывают параметры запроса и добавляют групповой префикс для группировки определенных параметров.Затем я использую полученную карту Map[String, String] и продолжаю обработку с классом формы, выполняя проверку и т. д., как обычно. Это позволяет мне использовать в сопоставлении вложенные классы case и снизить ограничение в 18 параметров.
Осторожно: впереди уродливый код! Вероятно, мне не следует показывать такой ранний хакерский код, но я надеюсь, что это поможет кому-то еще, кто хочет найти обходной путь.
def preprocessFormParams(prefix:String, replace:String)(implicit request:Request[AnyContent]):Map[String, String] = request.body.asFormUrlEncoded.map( _.filterKeys( _.startsWith(prefix)).map( m => m._1.patch(0, replace, prefix.length) -> m._2.head )).getOrElse(Map.empty)
def unprocessedFormParams(prefixes:Set[String])(implicit request:Request[AnyContent]):Map[String, String] = request.body.asFormUrlEncoded.map( _.filterKeys( !prefixes.contains(_) ).map( m => m._1 -> m._2.head )).getOrElse(Map.empty)
Таким образом, эти функции, вероятно, должны быть для понимания или разделены, но здесь идет: preprocessedFormParms принимает префикс и заменяет его:
val clientParams = preprocessFormParams("client_", "client.")
("client_id" -> "val1", "client_type" -> "val2") becomes ("client.id" -> "val1", "client.type" -> "val2")
Когда у меня есть параметры в форме group.key1, group.key2, я могу вложить классы case в такой форме
Form(mapping("client" -> mapping("type" -> nonEmptyText
"id" -> optional(nonEmptyText),
"secret" -> optional(nonEmptyText))
(RegisterClient.apply)(RegisterClient.unapply)
... more params ...)
(RegisterRequest.apply)(RegisterRequest.unapply)
В своем действии я иду вперед и отфильтровываю каждая из моих групп
implicit request =>
val clientParams = preprocessFormParams("client_", "client.")
val applicationParams = preprocessFormParams("application_", "application.")
val unprocessedParams = unprocessedFormParams(Set("client_", "application_"))
val processedForm = clientParams ++ applicationParams ++ unprocessedParams
Наконец, я могу применить свою форму, как обычно, но теперь я получаю вложенную структуру I, которая уменьшает количество аргументов и, надеюсь, делает класс case более управляемым.
clientRegistrationForm.bind(processedForm).fold( ... )
Используя этот подход, вы можете уменьшить количество параметров. Если ваши параметры не имеют того же префикса для простой группировки, что и моя проблема, вы все равно можете использовать тот же базовый подход, но фильтровать по другим критериям.