[БЕЗ_ЗВУКА] В этом видео мы рассмотрим, каким образом можно сделать валидацию параметров в Go. Начнём с того, что в Go нет встроенного валидатора никакого. Почему? Потому что валидация параметров — это очень специфичная вещь и разнится от проекта к проекту. Кто-то вырезает просто всё лишнее и подставляет дефолтные значения. Кто-то ругается ошибкой. Опять-таки ошибки тоже очень специфичны от проекта к проекту. И нет какой-то общепризнанной спецификации, что ошибки должны возвращаться вот так. Поэтому в Go не включили никакой стандартный пакет. Однако на просторах Интернета очень много разных пакетов для организации подобного рода действа. Итак. В данном примере мы рассмотрим два пакета. Валидатор, который умеет только валидировать значения, он не может ниоткуда их ни спарсить, ничего. Ему просто мы передаём на вход структуру, и он говорит да или нет, и если нет, то почему. И парсинг параметров. Мы воспользуемся пакетом компонентов gorilla, там очень много всякого. В данном случает пакет schema занимается тем, что парсит параметры из url, из urlvalues в структуру. Итак, для начала посмотрим, что мы, собственно, будем валидировать. У нас есть структура SendMessage. Представим, что мы какой-то почтовый сервис, и мы хотим отправить письмо. Есть Id, и поля валидатора указаны в struct tags по метке valid. По метке schema указаны поля для парсинга параметров в эту структуру. Итак, Id. Id у нас поле опционально, то есть оно может быть, либо его может не быть. Если его не будет, ругаться не будет. Priority. Priority — это строковое поле, которое может иметь три значения. Это валидатор in и low, high, normal, собственно, сами значения полей, которые могут быть. Recipient. Recipient извне, из параметров, которые я передам в url, должен прийти в поле to. За это будет нам отвечать schema, я в schema указываю, что это поле называется так-то. И валидатор там будет email. То есть если мне придёт невалидный e-mail, то валидатор ругнётся. Subject. Subject использует мой собственный валидатор, который я зарегистрировал специально для этих целей. Поле Inner. Оно, обратите внимание, оно публичное, начинается с большой буквы, но я не хочу туда ничего парсить и никак его валидировать, поэтому я и в schema и в поле valid указываю минус. Ещё есть поле flag. Для поля flag я ничего не указываю, потому что это приватное поле, оно называется с маленькой буквы, а поскольку оба эти механизма, и валидация, и парсинг, они работают через Reflect, то они не могут достучаться до этого поля, оно им не видно. Поэтому если я даже передам этот параметр, он не запишется. Итак, как теперь выглядит это в коде? Начнём с демонстрации. Вот то, что я ввёл. to, которое у меня превратилось дальше в Recipient; priority subject поля inner, которое я сказал игнорировать, обратите внимание, оно у нас никакое, и поле flag тоже, оно у нас имеет дефолтное значение. Ну и всё. Сообщение корректно. То есть всё хорошо. Теперь же, если я, например, попробую в Priority поменять его на что-то другое, например, qqq, то оно распарсилось в параметры, то есть Priority само значение есть, однако валидацию оно уже не прошло. Поле валидатора вернуло мне ошибку, что для такого-то поля, в данном случае Priority, оно не прошло валидацию. И если текстом это выводить, то: «Priority: qqq не валидируется как in(low | normal | high)». Рассмотрим в коде, каким образом можно это организовать. Итак, для начала я вывожу, собственно, саму строчку запроса, чтобы её было видно на экране, потом я создаю сообщение, дальше я создаю декодер для того, чтобы распарсить значение из входящих параметров в мою структуру. Обратите внимание, я говорю этому декодеру, что, пожалуйста, на неизвестные поля, на них не надо ругаться и их можно просто игнорировать. Так. Дальше я вывожу само сообщение, которое у меня уже распарсилось. И я вызываю ValidateStruct. Первым параметром оно возвращает булевую переменную «да» или «нет», но в данном случае оно мне не нужно, я смотрю только на ошибку. Если случилась ошибка, я могу вывести её просто на экран, там будет весь текст. Это не очень информативно. Вот это, потому что парсить руками, чтобы понять, какое поле было не так... Разве что вы захотите пользователю показывать именно такую строчку. Поэтому мы пытаемся достучаться при помощи преобразования интерфейса (err — это интерфейс) до реальных ошибок, которые там есть, преобразованием интерфейса. Если преобразование получилось, всё хорошо, то я итерируюсь по этим ошибкам и вывожу полностью эту ошибку на экран. Я могу там указать имя, достучаться до имени, достучаться до произвольного текста ошибки и посмотреть вообще, что там была за ошибка. Поэтому если вы хотите организовывать какого-то рода возврат о том, что было неправильно, то вот это вам хорошо поможет. Ну и дальше в функции init я покажу, что я зарегистрировал свой собственный валидатор "msgSubject". Дело в том, что у вас не всегда ваши типы данных, хоть они и имеют стандартный тип, но они не всегда будут влезать в те методы, которые предоставляет стандартный валидатор. Например, через него нельзя проверить динамический enum, то есть список значений. Я выше его писал, но это статичный список, а если я хочу динамику, уже не получится. Поэтому я зарегистрировал свой собственный обработчик, и я в нём получаю Subject, пытаюсь его преобразовать к строке, ну и смотрю, что если длина 0 или если длина больше 10, то я буду ругаться. Давайте проверим. Попробуем дописать что-нибудь в Subject. Поле Subject, оно невалидно. Соотвественно, я уже могу что-то с этим делать. Как это работает внутри? Работает внутри это через рефлексию, как и вся динамика, практически, в Go. Поэтому там есть хоть маленький, но и, тем не менее, overhead. Если же вы хотите там делать совсем-совсем быстро, то добро пожаловать в мир кодогенерации, как всегда. За счёт кодогенерации вы можете пройтись по даже нераспаршенным в структуре параметрам, вот по этой строчке, и в один проход и сразу спарсить её структуру, и сразу провести валидацию. Это будет очень быстро, однако вам придётся заморочиться с написанием кода, который это организует. Что делать? Использовать ли стандартный валидатор либо заморачиваться и писать свой? Решать вам. Однако лучше всё-таки начать с тула стандартного, и если вдруг он вылезет при профилировании, или вы заметите, что всё-таки ваша программа тормозит именно из-за него, что будет, скорее всего, очень-очень редко, то только тогда стоит что-то оптимизировать, потому что, как вы знаете, предварительная оптимизация — это зло.