Как разработать веб-приложение уровня Enterprise Application с нуля. Часть 3
В этой статье мы говорим о том, как валидировать данные, которые отправляют пользователи веб-приложению уровня Enterprise Application.
2К открытий2К показов
Поговорим о том, как валидировать данные, которые отправляют пользователи нашему веб-приложению. Это — третья статья из цикла о том, как разработать коммерческое веб-приложение с нуля (Часть 1, Часть 2).
Алексей Соломонов
СТО подписки «Огонь»
- Вводная.
- Требования к валидации проекта.
- Валидатор.
- Middleware.
- Правила валидации запросов
- Тестирование.
- Итоги.
Валидировать данные запросов необходимо потому что:
- Пользователи могут не вводить обязательные данные без которых работа приложения невозможна.
- Пользователи могут вводить неверные данные из-за невнимательности.
- Злоумышленники могут целенаправленно вводить неверные данные.
Итог всех этих действий один — приложение может работать неверно, данные могут быть не консистентны.
Требования к валидации проекта
Когда команда разрабатывает приложение, создается единый набор правил валидации для разных типов данных, например:
- Имя должно содержать только буквы и иметь длину не более 30 символов.
- Дата должна соответствовать формату RFC3339.
- Телефонный номер должен соответствовать международным рекомендациям.
Следовать стандартам и рекомендациям хорошо, потому что:
- Под стандарты пишутся библиотеки кода, которые позволять работать с этими типами данных.
- Если необходимо интегрировать несколько серверных приложений, то интеграция будет проще, когда приложения работают с данными, отвечающим каким-либо стандартам.
Теперь, когда мы поняли что валидация необходима и она должна опираться на стандарты, давайте добавим ее в наше приложение.
Валидировать данные можно двумя путями:
- На уровне описания протокола.
- На уровне приложения.
Валидация на уровне описания протокола
Наше приложение использует protobuf, поэтому было бы заманчиво описать все правила валидации на уровне протокола. К тому уже есть готовые плагины для protoc, которые позволяют добавить правила валидации в описание сообщений:
У валидации на уровне протокола есть три проблемы:
- Одни и те же правила (например дата должна быть в формате RFC3339) необходимо добавлять в разные сообщения. Соответственно есть риск, что разработчик ошибется, и валидация будет работать неверно или не будет работать вообще.
- Для какого то типа данных необходимо изменить валидацию. Если вы не нашли способ, как не дублировать правила валидации, тогда ее изменение превратится в проблему.
- Допустим вы решили первые две проблемы, но в какой то момент времени ваше приложение будет интегрироваться с другим, которое умеет отдавать данные только в xml. Соответственно вам не удастся переиспользовать правила валидации и надо будет что то придумывать.
Валидацию на уровне протокола можно использовать, когда вы пишете узкоспециализированное приложение, которое никогда ни с кем ни при каких условиях не будет интегрироваться. Для всего остального есть валидация на уровне приложения.
Валидация на уровне приложения
В golang большое количество библиотек, осуществляющих валидацию данных, поэтому можно выбрать любую понравившуюся и добавить в проект. Стоит сказать, что со всеми подобными библиотеками есть одна проблема — они не умеют работать с архитектурой grpc «из коробки», поэтому придется писать адаптеры над ними.
Для написания адаптеров к нам на помощь приходят:
- google.golang.org/protobuf/reflect/protoreflect поможет нам итеративно получить значения по всем полям сообщения.
- github.com/go-playground/validator станет фундаментом, на котором мы построим механизм валидации.
Архитектура валидации будет иметь следующую логику:
- Запрос перехватывается middleware.
- Middleware получает правила валидации из MethodDescriptor.
- Если правила есть, выполняет проверку свойств сообщения.
- Если нет, передает обработку запроса дальше.
Валидатор
Для начала давайте опишем сам валидатор и форму:
Данный подход позволит нам использовать правила валидации как в middleware, так и в другом месте приложения.
Теперь давайте определимся с типами данных, которые будем валидировать:
- uuid — строка в формате UUID v4.
- телефонный номер — строка с префиксом «+» и длиной не более 15 символов.
- адрес доставки — строка длиной 255 символов.
- дата доставки — дата в формате RFC3339 не раньше сегодняшнего дня.
- сумма заказа — положительное число с плавающей точкой.
- перечисление — значение enum больше нуля и меньше или равно максимальному.
- сообщение — вложенное сообщение.
- массив сообщений — вложенный массив сообщений.
Теперь давайте реализуем валидаторы для разных форматов строк:
Протестируем получившиеся валидаторы:
По аналогии реализуем другие валидаторы:
Мы создали единый набор правил валидации, который мы будем переиспользовать в разных доменах.
Middleware
Теперь необходимо вписать наши валидаторы в архитектуру grpc.
Расширяем MethodDescriptor:
Реализуем новую middleware, которая будет брать форму из описания метода и валидировать запрос:
Теперь наши запросы будут валидироваться в middleware, не захламляйте бизнес-код.
Правила валидации запросов
Все подготовительные работы окончены, и мы можем описать правила валидации для конкретных запросов. Давайте опишем правила для создании заказа.
По аналогии опишем правила для других запросов:
Тестирование
Доработки кода приложения и инфраструктуры завершены, самое время протестировать наше приложение. Поднимаем приложение:
Открываем https://enterprise.application.local
Давайте зарегистрируем пользователя с невалидным телефоном.
Получили ошибку валидации запроса.
Теперь возьмем валидный номер телефона.
Пользователь зарегистрирован.
А теперь создадим заказ без отложенных товаров.
Получили ошибку валидации запроса.
Отложим товары и создадим заказ.
Заказ создан.
Тесты прошли успешно, валидация запросов отрабатывает, как требуется.
Итоги
Итак, в этой статье мы научились:
- Разрабатывать архитектуру валидации protobuf сообщений.
- Реализовывать единый набор правил валидации.
- Вписывать правила валидации в архитектуру grpc.
- Описывать правила валидации для конкретных запросов.
Как и в предыдущей статье, код представлен в репозитории.
В следующей статье мы обсудим следующие темы:
- Выбор драйвера СУБД.
- Репозиторный слой приложения.
- Моки в тестах.
2К открытий2К показов