# JSON-schema validation
# Валидация с JSON-SCHEMA
Сегодня мы рассмотрим эффективный и надежный инструмент для валидации данных - валидация с JSON-Schema. JSON-Schema – это спецификация, определяющая медиа-тип для описания структур данных на основании JSON. Она позволяет разработчикам описывать и валидировать формат и структуру JSON-данных в приложениях.
# Краткое описание работы JSON-Schema
JSON Schema предлагает несложный подход к валидации структуры данных в формате JSON. С его помощью можно создавать сложные правила валидации для совершенно различных форм в формате JSON, и в то же время делать это структурированно и легко воспринимаемо.
Работа JSON Schema основывается на простом принципе - описания форматов JSON-данных. Оно представляет собой JSON-объект, который содержит различные ключи, представляющие собой «схемы». Эти схемы определяют типы данных, создают ограничения на значения и устанавливают требования к присутствию определенных полей. Все это позволяет обеспечить строгую валидацию данных и гарантировать их стабильность.
Такая схема считается гибким и мощным инструментом, поэтому с помощью JSON Schema можно определить основные характеристики данных: обязательность поля, max и min допустимые значения, соответствует ли значение поля регулярному выражению и многое другое.
# Библиотека ajv (opens new window)
Для валидации мы будем рассматривать js-библиотеку ajv (opens new window). Эту библиотеку можно использовать для валидации наших данных на фронте(Vue, React, jQuery и др.) и на бэке (node.js).
# Установка:
npm install ajv
# Пример использования:
Рассмотрим простой пример подключения и использования библиотеки:
import Ajv from 'ajv'
const ajv = new Ajv()
const schema = {
type: 'object',
properties: {
foo: {
type: 'string',
}
},
required: [ 'foo' ],
additionalProperties: false,
}
const data = {
foo: 'test',
}
const valid = ajv.validate(schema, data) // true
if ( !valid ) {
console.log( ajv.errors )
}
В примере в самом начале импортируется библиотека и с помощью конструктора AJV создается ее инстанс. Далее объявляется константа schema - это объект, схема, в которой хранится описание структуры наших данных. В примере наши данные, которые мы будем валидировать, представлены в виде объекта data. Внутри схемы есть следующие параметры (название):
- type (
string) - тип наших данных, которые мы собираемся провалидировать. В нашем случае это объект; - properties (
object) - объект, в котором находятся поля нашей формы. ключ - название поля, значение - объект, в котором описывается соответствующее поле. в примере у полей описывается их тип. также там можно указывать min и max значения (для чисел), формат и регулярные выражения (для строк) и другое; - required (
String[]) - массив строк, в котором находятся названия обязательных полей нашей формы; - additionalProperties (
boolean) - указывает могут ли в нашей форме быть значения, которые не указываются в текущей json-схеме.
Валидация осуществляется с помощью метода validate, который первым параметром принимает схему, а вторым наши данные. Метод возвращает true, если валидация прошла успешна, в обратном случае false. В примере в случае ошибки валидации в консоль выводится список ошибок.
# Обработка ошибок
При неудачной проверке валидации массив ошибок будет доступен из свойства errors Ajv инстанса (в примере выше ajv.errors). Описание основных полей интерфейса ошибки :
interface ErrorObject {
keyword: string // keyword валидатора
instancePath: string // указывает путь до поля, в котором произошла ошибка, внутри наших данных (в формате JSON-pointer, например: '/foo')
schemaPath: string // указывает путь до keyword json-схемы (в формате JSON-pointer, например: '#/properties/foo/type')
params: object // содержит информацию об ошибке: keyword валидатора и название поля
message?: string // необязательное поле, сообщение об ошибке
}
Немного обновим нашу схему и данные из примера выше и посмотрим на реальный список ошибок. По умолчанию массив ошибок возвращает только первую ошибку, которая была найдена при валидации. Для отображения всех ошибок при объявлении инстанса ajv передадим параметр { allErrors: true }:
const ajv = new Ajv({ allErrors: true })
const schema = {
type: 'object',
properties: {
foo: {
type: 'string',
},
bar: {
type: 'number',
},
},
required: [ 'foo', 'bar' ],
additionalProperties: false,
}
const data = {
foo: 1,
baz: 'test',
}
const valid = ajv.validate(schema, data) // false
if ( !valid ) {
console.log( ajv.errors )
}
В консоли выведется следующий массив:
[
{
instancePath: "",
keyword: "required",
message: "must have required property 'bar'",
params: {
missingProperty: "bar",
},
schemaPath: '#/required',
},
{
instancePath: "",
keyword: "additionalProperties",
message: "must NOT have additional properties",
params: {
additionalProperty: "baz",
},
schemaPath: '#/additionalProperties',
},
{
instancePath: "/foo",
keyword: "type",
message: "must be string",
params: {
type: "string",
},
schemaPath: '#/properties/foo/type',
},
]
Как и ожидалось, наша форма не прошла валидацию по 3 причинам:
- Отсутствие обязательного поля
bar; - лишнее поле
baz; - несоответствие типа у поля
foo.
Этот ответ можно использовать для отображения сообщения ошибки пользователю
# $data reference
Если у нас в форме есть значения, которые зависят от значений из этой же формы, мы можем задать нужные ограничения с помощью $data reference.
const ajv = new Ajv({ $data: true })
const schema = {
properties: {
smaller: {
type: 'number',
maximum: {$data: '1/larger'},
},
larger: {type: 'number'},
},
}
const validData = {
smaller: 5,
larger: 7,
}
ajv.validate(schema, validData) // true
Для начала необходимо передать в инстанс ajv параметр { $data: true } . Далее в схеме мы указываем следующее условие: поле smaller должно быть не больше поля larger . Для этого в параметрах поля указываем ограничение maximum со значением объекта, в котором ссылаемся на нужное поле. Значение поля $data должно указываться в формате JSON-pointer (opens new window). Соответственно '1/larger' - это не деление, а путь до поля larger внутри формы validData. Подробнее можно ознакомиться в документации (opens new window).
# Валидация формата поля
Одна из полезных функций библиотеки - поддержка проверки формата полей. Для использования форматов необходимо будет установить дополнительный плагин - ajv-formats (opens new window).
Этот плагин позволяет проверять поля наших форм на следующие форматы: date, date-time, uri, email, hostname, ipv4, regexp, uuid и другие. Для использования всех форматов необходимо импортировать функцию addFormats и после объявления инстанса вызвать ее и передать сам инстанс:
import addFormats from 'ajv-formats'
const ajv = new Ajv()
addFormats( ajv )
При желании можно передать инстансу определенные форматы. Для этого вторым аргументом вызывается массив строк с нужными форматами:
addFormats( ajv, [ 'date', 'email' ] )
Также существует метод, для создания собственного формата:
ajv.addFormat("byte", {
type: "number",
validate: (x) => x >= 0 && x <= 255 && x % 1 == 0,
})
# Другие полезные фичи библиотеки
Я привел всего несколько особенностей этой библиотеки, которых оказалось достаточно для решения моей задачи. Помимо описанного выше, ajv также:
- поддерживает typescript;
- позволяет асинхронно подгружать схемы. Можно хранить схемы валидации для всего проекта в таблице БД. На фронте через бэк в нужный момент мы можем получать определенную схему и делать валидацию. Такой механизм может быть полезным, так как бэк cможет валидировать данные с помощью аналогичной json-схемы, используя свои инструменты (на node.js можно использовать ajv), например JSON.net (opens new window) для .net;
- позволяет описывать логику валидации с помощью нескольких json-схем, в которых описание полей может ссылаться друг на друга (https://ajv.js.org/guide/combining-schemas.html (opens new window));
- предоставляет плагин ajv-errors (opens new window), который позволяет составлять описание для сообщений об ошибках;
- предоставляет плагин ajv-i18n (opens new window) для поддержки интернационализации;
- и другое.
# Другие инструменты валидации
Очевидно, что говоря об инструментах валидации во Vue, сравнивая валидацию с помощью JSON-схем и vuelidate (opens new window), например, то схема позволит максимально точно, гибко и наглядно описать нужную структуру данных. При этом на маленьких проектах валидация с JSON-схемой может оказаться излишней.
# Вывод
JSON-схемы предлагают мощный подход к проверке структуры и типа данных JSON-объектов. Они могут использоваться для проверки всего, начиная от простых значений и заканчивая сложными, вложенными объектами. Вместе с тем, они предлагают гибкость, которая делает их подходящими для широкого спектра приложений. Для сервисов, которые
- используют и передают друг другу самые различные модели и структуры данных;
- для которых важна надежность в обмене данными;
- где есть многочисленные ограничения формата данных
это будет хорошим решением, так как повысит надежность кода и предсказуемость форматов данных. С такими инструментами как ajv можно легко настроить и интегрировать в свой сервис валидацию JSON-схемой на любом языке программирования.