Pull to refresh

Comments 71

Как альтернативу, подпадающие под все ваши вводные правила, могу порекомендовать Joi. Из плюсов — правил валидации намного больше, ну и чуть больше возможностей, например не увидел у вас, возможно ли задавать дефолтные значения переменных, если во входящий данных пусто и можно ли у вас задавать условия ветвления, если например я хочу допустить, что в одной переменной может быть строка или массив строк и соответственно задать разные правила для каждого из случаев.
Спасибо, Joi выглядит неплохо, но ему присущи проблемы других валидаторов:
1) Он только под JS. Нам нужно кроссплатформенное решение
2) Правила валидации описываются в виде кода, а не структуры данных (Проблема №3)
3) Останавливается на первой ошибке (Проблема №4)
4) Не нашел механизма для регистрации собственных правил
5) Сложно описывать иерархические правила (если это возможно)
6) так далее… )

Относительно большего количества правил, то мы старались в спеку LIVR включать необходимый минимум. LIVR легко расширяется и можно делать отдельные библиотеки правил.

Дефолтовые значения переменных — это хорошая идея. В LIVR очень легко реализуема путем добавления правила «default». И можно будет описывать

{ age: [ 'positive_integer', {default: 18} ] }


Если будет востребовано, то можно добавить правило и на уровень спеки
1) Ну да, это только JS. У вас есть php и python это круто. Раньше, когда на этих языках писал, то испытывал те же проблемы что и вы — не было нормальной библиотеки для валидации. Теперь, видимо, есть, спасибо :)
2) Можно описать в виде структуры (json), потом скомпилировать структуру в схему Joi.
3) Есть опция, которая позволяет отобразить все ошибки, а не остановится только на первой.
4) Если вы имеете ввиду, что для валидации данных необходимо использовать свою функцию, то для этого есть Joi.func
5) Можно валидировать неограниченное количество вложенных объектов.

Кстати, вот наверно чего не хватает всем библиотекам — это конвертирующих функций, которые исполняются после успешной валидации. В Joi они примитивны, например там в число преобразовать или в строку. Не хватает чего-нить типа такого:

convert: function (value) {
return value.toString().split('\n');
}

Выглядит так, что вы взяли формат данных, не обладающий схемой, а теперь героически с этим боретесь.
Вначале мы перепробовали разные валидаторы, долго с ними боролись, писали обвязки всякие. А затем появился LIVR и мы перестали бороться, и начали получать удовольствие :)
Отличное решение, возьму на заметку и скорее всего буду использовать совместно с бэк/фронт частью (в этом плане вообще отлично получилось, что одни правила работают на разных языках/плфтформах и их стоит прописать один раз). Однозначно +
Выглядит так, как будто вы не читали статью.
У нас, например, есть сканер, который собирает разную информацию. Сегодня в этом поле может быть число, завтра там строка, послезавтра там уже массив строк и т.п. То есть заранее не известно, что мы получим. При этом нам нужно обязательно хранить эту информацию, не важно какой бы она была. Понимаю, что это чисто наша спецефическая проблема, но всё же — заранее схему описать нельзя. Можно лишь попытаться подогнать пришедшие данные под допустимые схемы и сохранить их БД для последующей обработки.
Можно создать свое правило, которое будет смотреть на тип пришедших данных и решать, как его проверять. Если же глядя на тип данных нельзя решить, как их валидировать, то значит тут никакой валидатор не поможет :).

Немного схожий кейс — github.com/koorchik/LIVR/issues/9
И всё же «валидатор не должен делать ничего кроме валидации» (принцип единственной ответственности).
Пример, показывающий зависимость результата валидации от порядка следования правил, «хорошие валидаторы» так не делают.
Я бесспорно понял что Вы хотели сказать, но вы же сами задали правила валидации в таком порядке? И одно правило можно указать несколько раз… Я бы наверно даже остался в таком варианте и доверил регуляцию этих правил вышестоящему инструменту(защита от дурака)
На это мы пошли умышлено. Такой подход дает возможность организовать pipe. Вот пример.

Кроме того, это делает валидатор более предсказуемым, поскольку есть приоритет правил. На практике такой подход показал себя очень удобным.
Автору LIVR: посмотрите библиотеку dklab.ru/lib/HTML_MetaForm
Эта библиотека, конечно, не блещет новизной, да и способ реализации в ней сомнителен (не говоря уж о публичном интерфейсе класса), однако я не для того ссылку на нее привожу, чтобы сравнивать, а только потому, что, может быть, вы в ней найдете какие-то интересные идеи для своего проекта. Например, одна из идей — в том, что HTML-разметка формы сама по себе содержит уже много информации для валидаторов (например, что из селектбокса не может быть выбран несуществующий элемент, или что в hidden-поле или в атрибуте action формы значение не может меняться волшебным образом). В HTML есть полный список полей формы, MetaForm позволяет навешивать на них мета-атрибуты, одни из них — meta:validator — очень похож по смыслу на валидаторы, описаные в статье.
Спасибо. В целом концепция такая, что есть LIVR-валидатор, как ядро, и его можно использовать в более высокоуровневых библиотеках. Например, можно написать LIVR-Forms, который, будет смотреть на поля с атрибутом data-rules и использовать LIVR для их валидации. Или же тянуть метаинформацию с самой HTML разметки (селекты и тд)
А как вы храните все ваши схемы для валидации? В каждом проекте лежит свой набор схем или как-то централизованно?
В каждом проекте свой набор схем, централизированного хранилища нет. Кроме того каждый проект имеет набор каких-то специфических дополнительных правил, например, «company_id», или «query».

Относительно хранения схем в пределах проекта. Иногда мы описываем схемы в отдельных файлах. У нас есть инструмент для тестирования всяких REST API и там мы всегда описываем схему ожидаемого ответа в отдельных JSON файлах. И есть разные RESTful сервисы, где чаще просто непосредственно в коде и пишем правила. Если возникает необходимость переиспользовать правила, тогда можно выносить в отдельные файлы.
Объясните, пожалуйста, как вы отличаете такие два примера:
rules:
{
name: 'required',
}

data:
{
name: 'REQUIRED',
}

result:
{
name: 'REQUIRED',
}



и
rules:
{
name: 'required',
}

data:
{
name: '',
}

result:
{
name: 'REQUIRED',
}

?
Какую это играет роль, если, согласно статье, валидатор вернет только невалидные поля, т.е. result из второго примера? (в песочнице на сайте — на то она и песочница — будут отображены оба, различие заключается в подсветке)
написал в свое время аналогичный велосипед (для ноды), добавлю пару идей — не увидел у Вас в описании рекурсивности, то есть если роверяемый параметр — объект, со своей структурой, то как его проверять? Вроде вот этого(извините что без тегов):
var rules = {
«contracts»: {
«flags»: «required»,
«type»: «object»,
«rules»: {
//вложенные правила
}
},
«lots»: {
«flags»: «required»,
«type»: «object»,
«rules»: {
«id»: {
«flags»: «required»,
«type»: «number»
},
«contract_id»: {
«flags»: «required»,
«type»: «number»
},
«filter_id»: {
«flags»: «required»,
«type»: «number»
}

А, нет, вижу. nested_object — это оно, да?
И второй момент, вы почему то продолжаете традиции предыдущих валидаторов, создавая сто пятсот правил вместо простого match:
var rules = {
«mandate»: {
«flags»: «required»,
«type»: «string»,
«match»: "^[0-9a-f]{16}$",
}
};
Согласитесь, добрая часть правил спокойно реализуется через регулярки, зачем для этого лепить дестки/сотни сущностей?
И еще, по опыту. Для валидатора неплохо иметь возможность (по крайней мере мне это удобно в работе) задать политику — strict — первое же несовпадение с правилом — всех в лес, soft — все что не проходит по правилам — помечать либо возвращать в отдельном объекте.
Дело в том, что валидатор может использоваться немного шире чем простая проверка параметров GET запроса. Это и проверка параметров функции и соответствие структуры объекта шаблону и полученные с базы данные.
А так — целиком поддерживаю идею декларативной валидации, функциональщина в этом деле, на мой взгляд — зло.
По порядку:
1) Все верно, вложенные объекты описываются через «nested_object». В посте раздел «Метаправила» посвящен таким правилам. Кроме того, можно добавить свои дополнительные (как в этом примере github.com/koorchik/LIVR/issues/9)
2) Относительно регулярок. Это поддерживается, только называется «like». Но регулярки не всегда удобно читать, например легче зарегистрировать алиас «ip_address» для проверки айпишки и использовать его везде ( и возвращать какой-то понятный код ошибки ), чем везде вставлять регулярку.
3) strict режим идея неплохая. Например, у нас один из заказчиков хотел, что если придет любой лишний параметр в REST API, например, человек опечатался, то выдавать ему сразу ошибку про неподдерживаемый параметр. Подумает над этим.
>strict режим идея неплохая. Подумает над этим.
так, на всякий случай. У себя сделал это через исключения, то есть в стрикт режиме вообще ничего не возвращается, бросает ексепшн. Удобно, не надо дополнительно проверять результаты проверки (не навязываю, просто делюсь опытом)
Исключения добавляются достаточно просто, нужно просто обернуть все правила (есть в SYNOPSYS разделе для Perl реализации — metacpan.org/pod/distribution/Validator-LIVR/README.pod ). В Perl реализации выглядит так:
my $default_rules = Validator::LIVR->ger_default_rules();
 
while ( my ($rule_name, $rule_builder) = each %$default_rules ) {
    Validator::LIVR->register_default_rules($rule_name => sub {
        my $rule_validator = $rule_builder->(@_);
 
        return sub {
            my $error = $rule_validator->(@_);
            die $error if $error;
            return;
        }
    });
}


для других реализаций должно работать аналогично
целиком поддерживаю идею декларативной валидации, функциональщина в этом деле, на мой взгляд — зло.

[zanuda_mode]
Функциональное программирование — подмножество декларативного программирования.
[/zanuda_mode]
Возможно дико туплю, но где регэкспы и как их использовать?
Из спеки:
like

Be aware that regular expressions can be language dependent. Try to use most common syntax.
Вам не кажется, что в таком виде это просто не имеет смысла? Декларируется независимость от языка, но регулярки везде разные, никакого «common syntax» по сути нет, даже в самых тривиальных вещах где-то пишут «a+» а где-то «a\+». Не лучше ли в спеке однозначно объявить что синтаксис регулярок должен быть, например, PCRE?

Ещё вопрос по playground — я не разбирался с чем это связано, но в Firefox/Linux при попытке изменять значения полей в «Data for validation» после нажатия каждой кнопки заметно шуршит винт и firefox грузит CPU. Например, если в поле zip: непрерывно набирать и удалять цифры то винт трещит непрерывно и firefox ест 100% одного ядра CPU. Вроде бы проверка структуры на 5 полей, пусть даже выполняемая после ввода каждого символа, не должна так сильно грузить машину (причём не офисную, а разогнанный на 4.5GHz i7-2600K). Что и зачем при этом пишется на винт вообще не понятно — Вы что, каждое изменение по keyUp сохраняете куда-нить в localstorage?
Относительно регулярок. JS регулярки могут быть несовместимы с PCRE. Кроме того сами регулярки никак спецификацией не регламентированы. То есть, используется движок регулярных выражений, встроенный в язык. Единственное, что можно сделать в спеке — это рекомендовать не использовать очень хитроумные регулярки при описании правил, если хотите сохранить переносимость. Если переносимость не важна, тогда это не проблема.

Относительно подтормаживаний, livr-playground в localstorage ничего не пишет. Можно поиграться с исходниками, доступны на github -https://github.com/WebbyLab/livr-playground.
Ну, Вы-ж хотите чтобы один и тот же набор правил идентично выполнялся разными языками. Если за идентичность реализации кастомных проверок отвечают их разработчики, то вот за идентичность регулярок в данный момент отвечает спека. И по текущей спеке получится так, что из-за мелкого отличия в синтаксисе регулярки возникнет необходимость держать две копии правил — для фронта и для сервера… или для RPC сервиса написанного на одном языке и клиента к нему написанном на другом.
В теории, если стремиться к полной идентичности, тогда спецификация должна описывать полный синтаксис регулярок (и предоставлять набор тестов для движков регулярных выражений), а каждая реализация должна реализовать свой движок по спецификации. На практике же, использования движка регулярных выражений, встроенного в язык, не создает проблем. Но, на всякий случай, в спеку добавили ремрку «Try to use most common syntax.». Если следовать этому совету, то не нужно будет держать две копии правил.

Если же, на практике есть необходимость в полной идентичности, то всегда можно переопределить правило like, и разрешить в нем только определенный набор символов, например.
Может добавить для таких платформозависимых вещей опционально возможность указывать разные версии для разных платформ? Потому что вот только загорелся попробовать, но после замечания выше уже не буду, поскольку точно знаю, что на мои правила приходится писать похожие, но не идентичные регулярки на php и js.
Проблема высосана из пальца:
1) Регулярки в PHP, JS, Python и других языках очень близки. Очень редко нужны фичи регулярок, которые доступны только на одной платформе. Ок, в JS регулярках не поддерживается lookbehind, но много ли у вас таких регулярок? За такой совместимостью следить не большая проблема.
2) Просмотрел несколько наших проектов — «like» практически нигде не нужен. Намного удобней добавить свое правило, чтобы не копипастить регулярку по 10 модулям. Например, для проверки суммы, мы не используем «like», а создаем свое правило «positive_amount». Это как вынести повторяющийся код в функцию и дать ей нормальное название. Например, у нас есть такие правила «valid_phone», «valid_url», «alphanumeric», «company_id», «uppercase_latin_symbols». Вместо всех них можно было везде писать было бы «like», но это значительно хуже в поддержке.
3) Определение своих правил позволяет выдавать более информативные ошибки. Например, «positive_amount» у нас возвращает WRONG_AMOUNT_FORMAT, AMOUNT_TOO_SMALL, AMOUNT_TOO_LARGE. Эти ошибки затем локализируются и показываются в виде сообщений пользователю. Это намного информативней, чем просто WRONG_FORMAT, который возвращает «like».
4) Можно написать пару тестов для кастомных правил (да и для бизнес-логики тоже), что будет полезно в любом случае.
5) Можно переопределить «like» (или добавить какое-то свое правило, типа «match») и разрешить принимать регулярки (или просто wildcards) только в определенном формате.
6) Указывать версии для разных платформ не даст особого выигрыша, поскольку очень низка вероятность, что в каждой версии вы решите использовать фичи доступные только на конкретной платформе. Одна из версий у вас будет работать на всех платформах, ее и оставляйте.
7) Мир не идеален, абстракции текут, но здравый смысл всех спасет :). То есть, если не вдаваться в крайности и решать практические задачи, то все работает.
8) Спецификация не является чем-то закрытым — она развивается, принимаются пожелания и замечания. Если при решении практических задач вы упретесь в ограничения LIVR, это будет отличным поводом для развития спецификации.
9) У нас в компании LIVR круто себя показал, даже в тех проектах, где он используется только для бекенда. По принципу «Learn once, use everywhere».
10) Инструмент выбирается из задач и я верю, что есть ситуации, когда LIVR может и не подойти )
А это нормально, что до и после email можно вводить любые символы?
пример
В целом, да. В email разрешен юникод.
В JS реализации не идеальная проверка. Хотя Perl-реализации полностью по RFC. Спасибо, добавим пару доп. тестов в тестовый комплект спецификации и попросим авторов реализаций обновить тесты.  
Обновили тесты, исправили, проявлялось только в JS реализации.
Вы правы, в случае с десятичными положительные числа начинаются не с единицы :). Спасибо — исправим.
Исправили, проявлялось только в JS реализации. Расширили тестовый комлпект для спеки.
Иногда встречается ситуация, когда правило валидации нельзя однозначно отнести к конкретному элементу формы. Например, в форме есть email и номер телефона, а для продолжения нужно заполнить хотя бы одно из этих двух полей. Позволяет ли LIVR каким-то образом описать такое правило, и если да, то к какому из полей оно относится?
Да, это возможно и делается достаточно просто:

var LIVR = require('livr');

var requiredIfFieldEmpty = function(field) {
    return function(value, fields) {
        var isDependentFieldEmpty = fields[field] === null || fields[field] === undefined || fields[field] === '';
        var isTargetFieldEmpty = value === null || value === undefined || value === '';

        if (isDependentFieldEmpty && isTargetFieldEmpty) {
            return 'REQUIRED'
        }
    }
};

LIVR.Validator.registerDefaultRules({ required_if_field_empty: requiredIfFieldEmpty});

var validator = new LIVR.Validator({
     phone: {required_if_field_empty: 'email'},
     email: {required_if_field_empty: 'phone'},
});
Попробуйте описать этот пример на JSON Schema


Могу ошибаться, но разве такая схема не подходит?
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
        "order_id": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true
        },
        "product_ids": {
            "type": "array",
            "items": {
                "type": "integer",
                "minimum": 0,
                "exclusiveMinimum": true
            }
        }
    },
    "required": ["order_id", "product_ids"]
}


При этом, свой тип можно определить в definitions:
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "definitions": {
        "positiveInteger": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true
        }
    },
    "properties": {
        "order_id": { "$ref": "#/definitions/positiveInteger" },
        "product_ids": {
            "type": "array",
            "items": { "$ref": "#/definitions/positiveInteger" }
        }
    },
    "required": ["order_id", "product_ids"]
}


Софта для json-schema больше и он есть для большего количества языков. Да и это более известная и популярная штука, как мне кажется.
Забыл дописать, а как будет выглядеть, например, подобная схема на LIVR?
Пример с оф. сайта json-schema
{
    "id": "http://some.site.somewhere/entry-schema#",
    "$schema": "http://json-schema.org/draft-04/schema#",
    "description": "schema for an fstab entry",
    "type": "object",
    "required": [ "storage" ],
    "properties": {
        "storage": {
            "type": "object",
            "oneOf": [
                { "$ref": "#/definitions/diskDevice" },
                { "$ref": "#/definitions/diskUUID" },
                { "$ref": "#/definitions/nfs" },
                { "$ref": "#/definitions/tmpfs" }
            ]
        },
        "fstype": {
            "enum": [ "ext3", "ext4", "btrfs" ]
        },
        "options": {
            "type": "array",
            "minItems": 1,
            "items": { "type": "string" },
            "uniqueItems": true
        },
        "readonly": { "type": "boolean" }
    },
    "definitions": {
        "diskDevice": {
            "properties": {
                "type": { "enum": [ "disk" ] },
                "device": {
                    "type": "string",
                    "pattern": "^/dev/[^/]+(/[^/]+)*$"
                }
            },
            "required": [ "type", "device" ],
            "additionalProperties": false
        },
        "diskUUID": {
            "properties": {
                "type": { "enum": [ "disk" ] },
                "label": {
                    "type": "string",
                    "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
                }
            },
            "required": [ "type", "label" ],
            "additionalProperties": false
        },
        "nfs": {
            "properties": {
                "type": { "enum": [ "nfs" ] },
                "remotePath": {
                    "type": "string",
                    "pattern": "^(/[^/]+)+$"
                },
                "server": {
                    "type": "string",
                    "oneOf": [
                        { "format": "host-name" },
                        { "format": "ipv4" },
                        { "format": "ipv6" }
                    ]
                }
            },
            "required": [ "type", "server", "remotePath" ],
            "additionalProperties": false
        },
        "tmpfs": {
            "properties": {
                "type": { "enum": [ "tmpfs" ] },
                "sizeInMB": {
                    "type": "integer",
                    "minimum": 16,
                    "maximum": 512
                }
            },
            "required": [ "type", "sizeInMB" ],
            "additionalProperties": false
        }
    }
}

Относительно этой схемы, то тут нужно будет описать некоторые правила/алиасы (аналог oneOf). Пример хороший, создал тикет github.com/koorchik/LIVR/issues/14, воможно добавим пару правил на уровень спецификации в LIVR 0.5
Автор написал:
> Сложный формат для правил. Хочется, чтобы структура с правилами была максимально близка к структуре с данными. Попробуйте описать этот пример на JSON Schema

Он не ставил под сомнение возможность описать это в JSON Schema, он говорил что получится структура слабо похожая на структуру данных. Что собственно, и было вами доказано. Да и вообще простыня знатная получилась, раза в три длинней чем
{
    order_id: ['required', 'positive_integer'],
    product_ids: [{
       'list_of': [ 'required',  'positive_integer' ]
    }]
}
Да, схема многословная, но почему слабо похожая? Здесь поле описывается массивом, там — объектом. При этом в схеме сразу видно, какого типа должно быть поле. В схеме поля объекта находятся по ключу properties, что позволяет добавить дополнительные условия на весь описываемый объект, вроде additionalProperties. Как тут запретить передавать поля, не описанные в схеме. Я увидел только что они просто игнорируются. А как быть, если эти поля нужно сохранять?

В этом примере мне не понятно, например, описание product_ids. Написано, что это список, состоящий из… А из чего он состоит? Из типа required и типа положительное число? Но required — это же не тип. Т.е. поле products_ids само по себе необязательное, но представляет из себя список, элементы которого обязательные и положительные числа. При этом, пустой список проходит валидацию. Зачем вообще тут нужен required?
Для меня более понятно было бы, если бы в списке list_of перечислялись допустимые типы в этом списке. Но для списка из разных элементов зачем-то сделан отдельный тип list_of_different_objects. Странно как-то.
Вам правда соит прочитать статью, такие вопросы возникают по тому что вы не разобравшись пытаетесь применить принципы своего любимого валидатора на livr, а он работает не так.

Цитирую автора (почти начало статьи):
Как описываются правила валиции? Каждое правило состоит из имени и аргументов (практически, как вызов функции) и в общем случае описывается следующим образом {«RULE_NAME»: ARRAY_OF_ARGUMENTS}. Для каждого поля описывается массив правил, которые применяются в порядке следования.
Все зависит от задач. JSON Schema имеет свои сисльные стороны и уникальные возможности. JSON Schema прекрасно подойдет в случаях, когда:
  • Вас не пугает многословный синтаксис
  • Нет потребовности в кастомных правилах со сложной логикой
  • Нет необходимости добавлять филтры, типа trim, to_lc и так далее
  • Нет потребоности в стандартизации ошибок
  • Нет нужно получать ошибки сразу для всех полей, включая вложенные структуры данных.


Но нужно, например, разрешить неописанные поля.

Относительно «additionalProperties», то в JSON Schema, по-умолчанию, разрешены все дополнительные поля. Что, я считаю, тоже недостатком:

Проблема №1. Многие валидаторы проверяют только те данные, для которых описаны правила проверки. Для меня важно, чтобы любой пользовательский ввод, который явно не разрешен, был проигнорирован. То есть, валидатор должен вырезать все данные для которых не описаны правила валидации. Это просто фундаментально требование.


По поводу правила «list_of», то это не так особенность валидатора, как правила. Валидатор очень гибкий и позволяет реализовать любые правила.

В общем, основной мой посыл в том, что инструмент подбирается под задачи и есть задачи, где JSON Schema удобней, а есть, где удобней будет LIVR
Да, все верно. Это совсем простой кейс, по которому можно сравнить описание на LIVR и JSON Schema. На LIVR получается явно лаконичние:

{
    order_id: ['required', 'positive_integer'],
    product_ids: [{
       'list_of': [ 'required',  'positive_integer' ]
    }]
}


Кроме того, остается остальные вышеупомянутые проблемы. Относительно софта — тоже верно. Например, пока нет LIVR для Ruby, но я думаю, если будет необходимость, найдутся желающие портировать.
Да, понятное дело, что лаконичнее. Это несомненно плюс, но мне кажется, из-за такой лаконичности какие-то сложные схемы будет трудно или невозможно описать на LIVR. Например, в json-schema для чисел есть проверка на делимость (multipleOf), для строк — regexp и т.д. И в схеме мне нравится то, что она самодостаточна и набор условий минимален, но из него можно слепить что-то сложное. Можно также вынести все свои типы в какой-то отдельный файл, а и через $ref их использовать.
В LIVR для сокращения строк кода введены некие готовые правила (например, positive_integer), хотя мы и сами могли бы их описать. А вот, например, negative_integer почему-то нет, это странно. Ещё в схеме есть patternProperties, позволяющий проверять имя ключа по регулярке, это порой бывает нужно.
В целом, для простых схем LIVR хорош, но сложные — пока что не его конёк.

Вот про софт ещё такой вопрос: из json-схемы можно генерировать html-формы (есть пара js-библиотек). Есть ли (и возможно ли) что-то подобное для livr?
«multipleOf» без проблем реализуется, но не хочется добавлять вещи в спеку, которые никто не будет использовать. Если уж возникнет такой случай, то в проекте всегда можно описать свое правило. LIVR легко расширить практически любой функциональностью. Не знаю возможно ли такое в JSON Schema. Относительно описания своих типов и сохранения их в отельный файл, то для этого отлично походят алиасы.

Относительно генирации html-форм, то можно было бы попробовать написать библиотеку, но на моей практике от таких инструментов для меня никакой пользы не было.
мне кажется, из-за такой лаконичности какие-то сложные схемы будет трудно или невозможно описать на LIVR

LIVR это больше чем просто набор стандартных правил, одна из основных идей в том чтоб можно было легко добавлять свои. В стандартной поставке идут только те, которые будут нужны почти всем.

Например, в json-schema для чисел есть проверка на делимость (multipleOf)
Ещё в схеме есть patternProperties, позволяющий проверять имя ключа по регулярке, это порой бывает нужно

Вот примеры правил которые редко бывает нужны. Их можно легко описать самому. При этом правило пишется на обычном языке программирования, и соответственно можно реализовать что угодно (например валидацию, которая зависит от значения получаемого из БД) в отличии от.

для строк — regexp

см. правило like.

Можно также вынести все свои типы в какой-то отдельный файл, а и через $ref их использовать

Регистрируете свои правила, потом их используете.

В целом, для простых схем LIVR хорош, но сложные — пока что не его конёк.

Ну и далее в том же духе. Вы просто не поняли основную идею, возможно стоит еще раз прочитать статью?
А если зарегистрировать правило на JS, его можно будет использовать в python-коде. Например, есть библиотека с кучей классных правил, но на js, а мой проект на питоне. Смогу ли я использовать эту библиотеку? Как я понял — нет. Тогда где здесь независимость от языка? Или как-то можно взять реализацию того же strongPassword из статьи и каким-то образом использовать её и в программе на других языках (без установки какого либо интерпретатора JS).
Schema же декларативна, все правила описываются таким же json-кодом, поэтому она реально независима. Я могу взять схему и данные и всегда буду уверен, что на любой реализации результат будет одинаковым.
Да, нужно будет портировать правило на другой язык. На деле редко бывает нужно, да и правила это не огромные простыни кода, портирование которых может вызвать проблемы. Но сама схема при этом останется прежней. В этом и есть независимость. В JsonSchema это достигается по сути изобретением своего языка для описания правил, который во первых очень ограничен, во вторых его еще как-никак нужно учить.
Для такой задачи в LIVR 0.4 были добавлены алиасы. Когда правило можно скомбинировать на базе других, дать ему название и дать свой код ошибки (опционально). Естественно, это не покрывает все ситуации, но многие проблемы решает.
Вышел LIVR 2.0 (http://livr-spec.org/) С новых фич:

Согласованный подход к работе с типами.
«Base rules» переименовали в «Common rules».
«Filter rules» переименовали в «Modifiers». Поскольку они ничего не валидируют, а только модифицируют данные.
«Helper rules» переименовали to «Metarules»
Добавлено правило «any_object», проверяет, что это объект
Добавлено правило строка — «string», просто строка любого типа
Добавлено правило «eq» — проверяет на строковое равенство
Добавлено метаправило «variable_object». Динамически определяет, какую валидацию использвоать в зависимосте от полей в объекте.
Добавлено метаправило «or». Позволяет применять правила поочередно до первого совпадения.
Добавлен модификтор «default» для установки значений по-умолчинаю, если пользователь ничего не передал.
Значительно расширен комплект тестов.

JavaScript реализация уже поддерживает все новые функции, остальные реализации в процессе обновления.
UFO just landed and posted this here
1-2. Да. См. комментарий выше
> Добавлено метаправило «or». Позволяет применять правила поочередно до первого совпадения.

3-4. Да. на больших проектах возникает и в более сложных кейсах, это все и многое другое можно реализовать зарегистрировав свое правило.
UFO just landed and posted this here
Да. Одним из способов это сделать является алиасы правил. Регистрируешь свое правило для того что будет одинаково, используешь как обычно, там где надо добавляешь дополнительные правила. См пункт «Работа с псевдонимами и регистрация собственных правил» выше.
UFO just landed and posted this here

Это похоже на "Выстрел дробью". Поддерживать потом такой набор правил...

UFO just landed and posted this here

Найти поле и добавить в него несколько правил, проще, чем найти несколько списков и добавить в каждый имя поля.

Нет, так нельзя. Но можно до передачи в валидатор преобразовать эту структуру в то что он понимает.

С таким доходом есть ряд проблем:


  1. Не ясно, как адресовать вложенные структуры. Можно конечно писать {required: ['address.city']} или {required: ['address/city'], но нужно помнить про массивы и тд.
  2. Не ясно в каком порядке применяются правила. Например, {required: ['email'], trim: ['email'] }. Конечно можно записывать [{required: ['email']}, {trim: ['email'] }], но все равно остается вопрос приоритета, если у меня уже есть уточняющие правила в основной структуре, типа {email: 'to_lc'}. В каком порядке применятся дополнительные правила? Конечно можно решить, что приоритет такой вот, но это не всем подойдет, во-вторых, нужно всегда помнить про этот приоритет.
  3. Вопрос безопасности. Если сейчас я удалю валидацию для поля, то поля будет вырезано валидатором и могу быть уверен, что никто ничего в нем не передаст. Если же описывать в перевернутом виде, то нужно будет постоянно помнить, что где-то это поле могло быть упомянуто.

Кроме того, можно сделать функцию типа applyCommonRules(livr, commonRules) и просто преобразовывать один формат в другой. Мощь LIVR в том, что это просто структура данных и можно делать с этими данными любые операции. Написать такую функцию займет полчаса времени и позволит увидеть в реальном проекте нужно ли это на уровне спецификации или может быть просто сторонней библиотекой (ну и ее можно зарелизить в opensource)

Sign up to leave a comment.

Articles