Перейти к содержимому

Validation set зачем нужен

  • автор:

Множество валидационное (Validation set)

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

Валидационное множество обеспечивает объективную оценку при настройке параметров архитектуры модели (например, количество скрытых нейронов в нейронной сети с целью уменьшения эффекта переобучения).

Одним из наиболее эффективных методов формирования и применения валидационных множеств является перекрёстная проверка.

Настройка гиперпараметров

Гиперпараметр (англ. hyperparameter) — параметр, который не настраивается во время обучения модели. Пример гиперпараметра — шаг градиентного спуска, он задается перед обучением. Пример параметров — веса градиентного спуска, они изменяются и настраиваются во время обучения.

Для подбора гиперпараметров необходимо разделить датасет на три части:

  • тренировочный набор данных (англ. training set), для обучения модели
  • валидационный набор данных (англ. validation set), для расчета ошибки и выбора наилучшей модели
  • тестовый набор данных (англ. test set), для тестирования выбранной модели

Зачем нам нужен и валидационный, и тестовый набор? Дело в том, что модель может переучиться на валидационном наборе данных. Для выявления переобучения используется тестовый набор данных.

Рассмотрим модель KNeighborsClassifier из библиотеки sklearn. Все “параметры” данной модели (loss, penalty, alpha и т.д), с точки зрения машинного обучения, являются гиперпараметрами, так как задаются до начала обучения.

class sklearn.linear_model.SGDClassifier(loss='hinge', penalty='l2', alpha=0.0001, l1_ratio=0.15, fit_intercept=True, max_iter=1000, tol=0.001, shuffle=True, verbose=0, epsilon=0.1, n_jobs=None, random_state=None, learning_rate='optimal', eta0=0.0, power_t=0.5, early_stopping=False, validation_fraction=0.1, n_iter_no_change=5, class_weight=None, warm_start=False, average=False)

Поиск по сетке

Общая информация

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

Поиск по сетке в Sklearn: использование

Пример использования GridSearch из библиотеки scikit-learn:

  1. Создание экземпляра класса SGDClassifier (из sklearn)
  2. Создание сетки гиперпараметров. В данном случае будем подбирать коэффициент регуляризации, шаг градиентного спуска, количество итераций и параметр скорости обучения.
  3. Создание экземпляра класса кросс-валидации
  4. Создание экземпляра GridSearch (из sklearn). Первый параметр — модель, второй — сетка гиперпараметров, третий — функционал ошибки (используемый для контроля качества моделей по технике кросс-валидации), четвертый — кросс-валидация (можно задать количество фолдов, а можно передать экземпляр класса кросс — валидации)
  5. Запуск поиска по сетке.
classifier = linear_model.SGDClassifier(random_state = 0, tol=1e-3)

parameters_grid =

cv = model_selection.StratifiedShuffleSplit(n_splits=10, test_size = 0.2) grid_cv = model_selection.GridSearchCV(classifier, parameters_grid, scoring = 'accuracy', cv = cv) grid_cv.fit(train_data, test_data)
Out: GridSearchCV(cv=StratifiedShuffleSplit(n_splits=10, random_state=0, test_size=0.2, train_size=None), error_score=nan, estimator=SGDClassifier(alpha=0.0001, average=False, class_weight=None, early_stopping=False, epsilon=0.1, eta0=0.0, fit_intercept=True, l1_ratio=0.15, learning_rate='optimal', loss='hinge', max_iter=1000, n_iter_no_change=5, n_jobs=None, penalty='l2. 'eta0': array([1.00000000e-05, 1.64285714e-05, 2.28571429e-05, 2.92857143e-05, 3.57142857e-05, 4.21428571e-05, 4.85714286e-05, 5.50000000e-05, 6.14285714e-05, 6.78571429e-05, 7.42857143e-05, 8.07142857e-05, 8.71428571e-05, 9.35714286e-05, 1.00000000e-04]), 'learning_rate': ['optimal', 'constant', 'invscaling'], 'max_iter': array([5, 6, 7, 8, 9])>, pre_dispatch='2*n_jobs', refit=True, return_train_score=False, scoring='accuracy', verbose=0)

Поиск по сетке в Sklearn: важные атрибуты

  • best_estimator_ — лучшая модель
  • best_score_ — ошибка, полученная на лучшей модели.
  • best_params_ — гиперпараметры лучшей модели
print(grid_cv.best_estimator_) 
Out: SGDClassifier(alpha=4.857142857142857e-05, average=False, class_weight=None, early_stopping=False, epsilon=0.1, eta0=1e-05, fit_intercept=True, l1_ratio=0.15, learning_rate='optimal', loss='hinge', max_iter=6, n_iter_no_change=5, n_jobs=None, penalty='l2', power_t=0.5, random_state=0, shuffle=True, tol=0.001, validation_fraction=0.1, verbose=0, warm_start=False)
print(grid_cv.best_score_) 
Out: 0.9099999999999999

print(grid_cv.best_params_)
Out:

  • cv_results_ — результаты всех моделей.

print(grid_cv.cv_results_)
Out:

print(grid_cv.cv_results_['param_max_iter'].data) 
Out: array([5, 6, 7, . 7, 8, 9], dtype=object)

Реализация поиска по сетке в библиотеках

Случайный поиск по сетке

Основная информация

Случайный поиск по сетке (англ. Random Grid Search) вместо полного перебора работает с некоторыми, случайным образом выбранными, комбинациями. На основе полученных результатов, происходит сужение области поиска.

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

Реализация случайного поиска по сетке

Последовательная оптимизация по модели

Основная информация

Последовательная оптимизация по модели (англ. Sequential Model-Based Optimization, SMBO) используются когда оптимизация целевой функции будет стоить очень «дорого». Главная идея SMBO — замена целевой функции «суррогатной» функцией.

На каждом шаге работы SMBO:

  1. Строится вероятностная модель (суррогатная функция) целевой функции.
  2. Подбираются гиперпараметры, которые лучше всего подходят для вероятностной модели.
  3. Подобранные гиперпараметры применяются к целевой функции.
  4. Вероятностная модель перестраивается (обновляется).
  5. Шаги 2-4 повторяются столько раз, сколько задал пользователь.

Существует четыре ключевые аспекта SMBO:

  • Сетка значений гиперпараметров (область поиска).
  • Целевая функция (выводит оценку, которую мы хотим минимизировать или максимизировать).
  • Вероятностная модель целевой функции (суррогатная функция).
  • Критерий, называемый функцией выбора (для выбора следующих гиперпараметры по текущей вероятностной модели).

Методы SMBO отличаются между собой вероятностными моделями и функциями выбора:
Популярные вероятностные модели (суррогатные функции):

  • Гауссовские процессы
  • Древовидный парзеновский оценщик
  • Регрессия случайного леса

Древовидный парзеновский оценщик

Основная информация

Как было написано выше, методы SMBO отличаются тем, как они строят вероятностную модель [math] [/math] . В случае древовидного парзеновского оценщика (англ. Tree-structured Parzen Estimator, TPE), используется следующая функция:

[math] [/math] — распределение гиперпараметров, [math] y [/math] — значение целевой функции, [math] y* [/math] — пороговое начение

[math] p(x|y) = \begin l(x), & \mbox y \lt y* \\ g(x), & \mbox y \ge y* \end [/math]

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

Алгоритм
  1. На вход подается список пар (parameters, loss)
  2. По заданному порогу, происходит разбиение списка на 2 части
  3. Для каждого списка строится распределение
  4. Возвращается значение: [math] argmin_ \frac[/math]

Последовательная конфигурация алгоритма на основе модели

Основная информация

Последовательная конфигурация алгоритма на основе модели (англ. Sequential Model-based Algorithm Configuration, SMAC) расширяет подходы SMBO:

  • Использует дискретные и условные пространства параметров.
  • Обрабатывает негауссовский шум.
  • Выделяет бюджет на общее время, доступное для настройки алгоритма, а не на количество оценок функций.

Кроме того, SMAC использует переданную ему модель для формирования списка перспективных конфигураций (сочетаний) параметров. Чтобы оценить перспективность конфигурация [math] \theta [/math] , SMAC строит распределение результатов модели для [math] \theta [/math] . С помощью этого распределения, а также информации, о текущей лучшей конфигурации, SMAC вычисляет ожидаемое положительное улучшение [math] EI(\theta) [/math] . После нахождения [math] EI(\theta) [/math] необходимо найти конфигурацию с наибольшим значением [math] EI(\theta) [/math] . Эта задача приводит к проблеме максимизация значения на всем пространстве конфигураций. Другие методы SMBO максимизируют значения а случайной выборке из пространства конфигураций, что достаточно плохо работает в случае высокомерного пространства. SMAC применяет немного другой подход: выполняется несколько локальных и поисков и среди них выбираются все конфигурации с максимальным [math] EI(\theta) [/math] . И уже среди них производится новый поиск и выбирается лучшая конфигурация.

Реализация

  • SMBO: SMAC
  • TPE: Hyperopt
  • Гауссовские процессы: Spearmint, Scikit-optimize

См. также

  • Автоматическое машинное обучение
  • Бустинг, AdaBoost
  • Кросс-валидация
  • Поиск архитектуры нейронной сети

Примечания

Источники

  • Algorithms for Hyper-Parameter Optimization
  • Sequential Model-Based Optimization for General Algorithm Configuration
  • Bayesian optimization
  • Гауссовские процессы и байесовская оптимизация
  • GridSearchCV sklearn

Валидация модели

Большую роль в приложении играет валидация модели или проверка вводимых данных на корректность. Например, у нас есть класс пользователя, в котором определено свойство для хранения возраста. И нам было бы нежелательно, чтобы пользователь вводил какое-либо отрицательное число или заведомо невозможный возвраст, например, миллион лет.

Например, пусть у нас есть проект консольного приложения, в котором есть клас User:

public class User < public string Name < get; set; >public int Age < get; set; >public User(string name, int age) < Name = name; Age = age; >>

В программе мы можем проверять вводимые данные с помощью условных конструкций:

CreateUser("Tom", 37); CreateUser("b", -4); CreateUser("", 130); void CreateUser(string name, int age) < User user = new User(name, age); // проверяем корректность значения свойства Name // если его длина в диапазоне от 3 до 50, то оно корректно if (user.Name.Length >= 3 && user.Name.Length "); else Console.WriteLine("Incorrect name!"); // проверяем корректность значения свойства Age // если оно в диапазоне от 1 до 100, то оно корректно if (age >= 1 && age \n"); else Console.WriteLine("Incorrect age!\n"); > public class User < public string Name < get; set; >public int Age < get; set; >public User(string name, int age) < Name = name; Age = age; >>

Здесь предполагается, что имя должно иметь больше 1 символа, а возраст должен быть в диапазоне от 1 до 100. Однако в классе может быть гораздо больше свойств, для которых надо осуществлять проверки. А это привет к тому, что увеличится значительно код программы за счет проверок. К тому же задача валидации данных довольно часто встречается в приложениях. Поэтому фреймворк .NET предлагает гораздо более удобный функционал в виде атрибутов из пространства имен System.ComponentModel.DataAnnotations .

Итак, изменим касс User следующим образом:

using System.ComponentModel.DataAnnotations; public class User < [Required] [StringLength(50, MinimumLength = 3)] public string Name < get; set; >[Range(1, 100)] public int Age < get; set; >public User(string name, int age) < Name = name; Age = age; >>

Все правила валидации модели в System.ComponentModel.DataAnnotations определяются в виде атрибутов. В данном случае используются три атрибута: классы RequiredAttribute, StringLengthAttribute и RangeAttribute. В коде необязательно использовать суффикс Attribute , поэтому он, как правило, отбрасывается. Атрибут Required требует обзательного наличия значения. Атрибут StringLength устанавливает максимальную и минимальную длину строки, а атрибут Range устанавливает диапазон приемлемых значений.

Теперь изменим код программы:

using System.ComponentModel.DataAnnotations; CreateUser("Tom", 37); CreateUser("b", -4); CreateUser("", 130); void CreateUser(string name, int age) < User user = new User(name, age); var context = new ValidationContext(user); var results = new List(); if (!Validator.TryValidateObject(user, context, results, true)) < Console.WriteLine("Не удалось создать объект User"); foreach (var error in results) < Console.WriteLine(error.ErrorMessage); >Console.WriteLine(); > else Console.WriteLine($"Объект User успешно создан. Name: \n"); >

Здесь определен метод CreateUser, который принимает два значения и с их помощью создает объект User. В этом методе используются классы ValidationResult, Validator и ValidationContext, которые предоставляются пространством имен System.ComponentModel.DataAnnotations и которые управляют валидацией.

Вначале мы создаем контекст валидации — объект ValidationContext . В качестве первого параметра в конструктор этого класса передается валидируемый объект, то есть в данном случае объект User.

var context = new ValidationContext(user);

Собственно валидацию производит класс Validator и его метод TryValidateObject() . В этот метод передается валидируемый объект (в данном случае объект user), контекст валидации, список объектов ValidationResult и булевый параметр, который указывает, надо ли валидировать все свойства.

var results = new List(); if (!Validator.TryValidateObject(user, context, results, true)) < //. >

Если метод Validator.TryValidateObject() возвращает false , значит объект не проходит валидацию. Если модель не проходит валидацию, то список объектов ValidationResult оказывается заполенным. А каждый объект ValidationResult содержит информацию о возникшей ошибке. Класс ValidationResult имеет два ключевых свойства: MemberNames — список свойств, для которых возникла ошибка, и ErrorMessage — собственно сообщение об ошибке.

Для тестирования три раза вызываем метод CreateUser, передавая в него сначала корректные, а потом некорректные данные:

CreateUser("Tom", 37); CreateUser("b", -4); CreateUser("", 130);

И мы получим следующий консольный вывод:

Объект User успешно создан. Name: Tom Не удалось создать объект User The field Name must be a string with a minimum length of 3 and a maximum length of 50. The field Age must be between 1 and 100. Не удалось создать объект User The Name field is required. The field Age must be between 1 and 100.

В первом вызове метода CreateUser передаются корректные данные, поэтому никаких ошибок при валидации не возникнет.

А во втором вызове CreateUser валидация завершится неудачно, так как свойствам User переданы некорректные значения. Например, свойству Name передается значение «b», что не соответствует правилам атрибута [StringLength(50, MinimumLength = 3)] . Также значение свойства Age — -4 не соответствует правилам атрибута [Range(1, 100)] . Соответственно консоль отобразит ошибки для свойств Name и Age.

В третьем вызове CreateUser валидация также завершится неудачно — значение свойства Age — 130 также не соответствует правилам атрибута [Range(1, 100)] . Но, кроме того, свойству Name передается пустая строка — значение, которое не соответствует правилу атрибута [Required] . Данный атрибут требует обязательного наличия значения.

Если применяются типы record, то атрибуты валидации можно указать непосредственно перед определением свойства:

using System.ComponentModel.DataAnnotations; public record class User( [property: Required] [property: StringLength(50, MinimumLength = 3)] string Name, [property: Range(1, 100)] int Age );

В этом случае перед названием атрибута указывается оператор property:

Таким образом, вместо кучи условных конструкций для проверки значений свойств модели мы можем использовать один метод Validator.TryValidateObject() , а все правила валидации определить в виде атрибутов.

Валидация HTTP-запросов — Веб-разработка на Go

Представим, что мы разрабатываем социальную сеть, и нам нужно реализовать функцию публикации поста. Если не проверять данные, которые вводит пользователь, то мы можем получить пост с пустым или слишком длинным текстом. Обычно такие ошибки приводят к некорректному поведению в приложениях. Это не хорошо, потому что пользователь не сможет понять, что произошло, и нам придется разбираться, почему пост не опубликовался.

В этом уроке мы разберем, как проверять HTTP-запросы в Go. Это важно, потому что проверки позволяют избежать ошибок и обеспечить безопасность нашего приложения.

Ручная проверка запросов в Go

Процесс проверки запросов на корректность перед последующей обработкой называется валидацией:

У разработчиков есть несколько вариантов реализации валидации в Go. В некоторых проектах придерживаются идеологии простоты чтения кода и реализуют валидацию вручную.

Например, валидация запроса на сохранение поста может выглядеть следующим образом:

package main import ( "errors" "fmt" "github.com/gofiber/fiber/v2" "github.com/sirupsen/logrus" ) type CreatePostRequest struct  UserID int64 `json:"user_id"` Text string `json:"text"` > func (req *CreatePostRequest) Validate() error  if req.UserID  0  return errors.New("user ID cannot be less than 0") > if req.Text == ""  return errors.New("text is empty") > if len(req.Text) > 140  return errors.New("text is too long") > return nil > func main()  webApp := fiber.New() webApp.Post("/posts", func(ctx *fiber.Ctx) error  // Парсинг JSON-строки из тела запроса в объект. var req CreatePostRequest if err := ctx.BodyParser(&req); err != nil  return fmt.Errorf("body parser: %w", err) > // Проверка запроса на корректность. err := req.Validate() if err != nil  return ctx.Status(fiber.StatusUnprocessableEntity).SendString(err.Error()) > // @TODO Сохранение поста в хранилище. return ctx.SendStatus(fiber.StatusOK) >) logrus.Fatal(webApp.Listen(":80")) > 

Запускаем веб-приложение и отправляем запрос на создание поста с некорректными данными:

--location --request POST 'http://localhost/posts' \ --header 'Content-Type: application/json' \ --data-raw '' 

В данном запросе мы указали некорректный идентификатор пользователя и пустой текст поста. В ответ получаем сообщение об ошибке:

Попробуем отправить корректные значения:

--location --request POST 'http://localhost/posts' \ --header 'Content-Type: application/json' \ --data-raw '' 

В ответ приходит статус 200 OK, что означает успешное прохождение проверок.

Таким образом, мы настроили проверку запросов, которые приходят в наше веб-приложение. Если запрос на создание поста содержит некорректные данные, то мы возвращаем ошибку. В случае успешной валидации запроса мы возвращаем ответ со статусом 200 OK.

Со стороны кода в данной реализации все проверки описаны явно и легко читаются. Когда новый разработчик присоединится к проекту, он быстро поймет логику приложения, и как проверяются запросы.

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

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

Чтобы решить эти недочеты, следует использовать готовую библиотеку для валидации запросов. Мы рассмотрим самую часто используемую библиотеку в Go — go-playground/validator . Далее будем ее называть Validator.

Валидация запросов с помощью Validator

Библиотека Validator позволяет реализовать валидацию запросов с помощью аннотаций полей структур. Для каждого поля структуры мы описываем список правил проверок, которые необходимо осуществить. Например, валидация запроса на публикацию поста может выглядеть следующим образом:

package main import ( "fmt" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "github.com/sirupsen/logrus" ) type CreatePostRequest struct  // Описываем правила валидации в аннотациях полей структуры. UserID int64 `json:"user_id" validate:"required,min=0"` Text string `json:"text" validate:"required,max=140"` > func main()  webApp := fiber.New() validate := validator.New() webApp.Post("/posts", func(ctx *fiber.Ctx) error  // Парсинг JSON-строки из тела запроса в объект. var req CreatePostRequest if err := ctx.BodyParser(&req); err != nil  return fmt.Errorf("body parser: %w", err) > // Проверка запроса на корректность. err := validate.Struct(req) if err != nil  return ctx.Status(fiber.StatusUnprocessableEntity).SendString(err.Error()) > // @TODO Сохранение поста в хранилище. return ctx.SendStatus(fiber.StatusOK) >) logrus.Fatal(webApp.Listen(":80")) > 

Запускаем веб-приложение и отправляем запрос на создание поста с некорректными данными:

--location --request POST 'http://localhost/posts' \ --header 'Content-Type: application/json' \ --data-raw '' 

В данном запросе мы указали некорректный идентификатор пользователя и пустой текст поста. В ответ получаем сообщение об ошибке:

'CreatePostRequest.UserID' Error:Field validation for 'UserID' failed on the 'min' tag Key: 'CreatePostRequest.Text' Error:Field validation for 'Text' failed on the 'required' tag 

Попробуем отправить корректные значения:

--location --request POST 'http://localhost/posts' \ --header 'Content-Type: application/json' \ --data-raw '' 

В ответ приходит статус 200 OK, что означает успешное прохождение проверок.

Так мы реализовали валидацию запросов с помощью библиотеки Validator. Когда мы передаем некорректные данные, то в ответ получаем сообщение об ошибке. Оно позволяет понять, какие данные необходимо исправить. Если все данные заполнены правильно, то мы получаем статус 200 OK.

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

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

package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct  Email string `validate:"required,email"` > func main()  v := validator.New() // Вывод: Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required' tag fmt.Println(v.Struct(&User<>)) // Вывод: Key: 'User.Email' Error:Field validation for 'Email' failed on the 'email' tag fmt.Println(v.Struct(&UserEmail: "test">)) // Пустой вывод, так как ошибки нет. fmt.Println(v.Struct(&UserEmail: "test@gmail.com">)) > 

Полный список правил проверок можно смотреть в документации .

Пользовательские валидаторы

Готовые правила обычно покрывают большинство нужд в валидации запросов. Но иногда нужно добавить свои правила с пользовательской логикой. Для этого можно использовать функцию validate.RegisterValidation() .

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

package main import ( "fmt" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "github.com/sirupsen/logrus" "log" "strings" ) type CreatePostRequest struct  // Описываем правила валидации в аннотациях полей структуры. UserID int64 `json:"user_id" validate:"required,min=0"` Text string `json:"text" validate:"required,max=140,allowable_text"` > var forbiddenWords = []string "umbrella", "shinra", > func main()  webApp := fiber.New() validate := validator.New() vErr := validate.RegisterValidation("allowable_text", func(fl validator.FieldLevel) bool  // Проверяем, что текст не содержит запрещенных слов. text := fl.Field().String() for _, word := range forbiddenWords  if strings.Contains(strings.ToLower(text), word)  return false > > return true >) if vErr != nil  log.Fatal("register validation ", vErr) > webApp.Post("/posts", func(ctx *fiber.Ctx) error  // Парсинг JSON-строки из тела запроса в объект. var req CreatePostRequest if err := ctx.BodyParser(&req); err != nil  return fmt.Errorf("body parser: %w", err) > // Проверка запроса на корректность. err := validate.Struct(req) if err != nil  return ctx.Status(fiber.StatusUnprocessableEntity).SendString(err.Error()) > // @TODO Сохранение поста в хранилище. return ctx.SendStatus(fiber.StatusOK) >) logrus.Fatal(webApp.Listen(":80")) > 

Запускаем веб-приложение и отправляем запрос на создание поста с текстом, который содержит слово-фильтр:

--location --request POST 'http://localhost/posts' \ --header 'Content-Type: application/json' \ --data-raw '' 

В ответ получаем валидационную ошибку:

'CreatePostRequest.Text' Error:Field validation for 'Text' failed on the 'allowable_text' tag 

Если же отправить запрос с текстом без запрещенных слов, то получим успешный ответ:

--location --request POST 'http://localhost/posts' \ --header 'Content-Type: application/json' \ --data-raw '' 

В итоге мы описали пользовательское правило валидации по тегу allowable_text. Оно проверяет, что текстовое поле не содержит запрещенных слов.

Когда приходит запрос с запрещенным словом, валидация не проходит, а клиенту возвращается ошибка. Если в запросе передать корректный текст, то валидация пройдет успешно, и клиент получит ответ со статусом 200 OK.

Выводы

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

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *