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


Конфигурации

Открытым src/config и создать validation.ts файл.

// src/config/validation.ts

const validationConfigurations = {
  rules: {},
};

export default validationConfigurations;
Войти в полноэкранный режим

Выйти из полноэкранного режима

Просто пустой объект на данный момент, теперь давайте обновим индексный файл конфигурации, чтобы внедрить его в общие конфигурации.

// src/config/index.ts
import config from "@mongez/config";
import { databaseConfigurations } from "config/database";
import validationConfigurations from "./validation";

config.set({
  database: databaseConfigurations,
  validation: validationConfigurations,
});
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь можно получить доступ к конфигурациям из любой точки приложения.


Список правил

Помните rulesTypes что мы добавили в RulesList учебный класс? мы давайте удалим его и вместо этого будем использовать конфигурации.

// src/validation/rules-list.ts
import config from "@mongez/config";

export default class RulesList {
  /**
   * Errors list
   */
  protected errorsList: any = [];

  /**
   * Constructor
   */
  public constructor(
    protected readonly input: string,
    protected readonly value: any,
    protected readonly rules: any,
  ) {
    //
  }

  /**
   * Validate the rules
   */
  public async validate() {
    for (const ruleName of this.rules) {
      // the rule will be in the rules section under the validation object
      const RuleClass = config.get(`validation.rules.${ruleName}`);

      const rule = new RuleClass(this.input, this.value);

      await rule.validate();

      if (rule.fails()) {
        this.errorsList.push(rule.error());
      }
    }
  }

  /**
   * Check if validator fails
   */
  public fails() {
    return this.errorsList.length > 0;
  }

  /**
   * Check if validator passes
   */
  public passes() {
    return this.errorsList.length === 0;
  }

  /**
   * Get errors list
   */
  public errors() {
    return {
      input: this.input,
      errors: this.errorsList,
    };
  }
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Что мы обновили здесь мы удалили rulesTypes и мы заменили его на config.get('validation.rules.${ruleName}') который получит правило из конфигураций.

Теперь давайте обновим наши правила в файле конфигурации.

// src/config/validation.ts
import { RequiredRule, StringRule } from "core/validator";

const validationConfigurations = {
  rules: {
    [RequiredRule.ruleName]: RequiredRule,
    [StringRule.ruleName]: StringRule,
  },
};

export default validationConfigurations;
Войти в полноэкранный режим

Выйти из полноэкранного режима

Мы импортировали StringRule а также RequiredRule из которого мы еще не экспортировали validator папка, так что давайте сделаем это.

Помните статическое свойство ruleName? вот почему мы создали его, чтобы использовать его здесь с более причудливым кодом 🙂

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

Переименовать index.ts к validator.ts а теперь создайте index.ts файл.

// src/core/validator/index.ts
export { default as RulesList } from "./rules-list";
export { default as RequiredRule } from "./rules/required";
export { default as Rule } from "./rules/rule";
export { default as StringRule } from "./rules/string";
export { default as Validator } from "./validator";
Войти в полноэкранный режим

Выйти из полноэкранного режима

Мы экспортировали класс Validator, список правил, класс правил, классы RequiredRule и StringRule.

Поэтому, когда мы создаем новое правило, не забудьте экспортировать его из индексного файла.

Теперь обновите request.ts файл и обновить импорт из validator к core/validator.

// src/core/http/request.ts
import { Validator } from "core/validator";
// ...
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь попробуйте, он должен работать так, как ожидалось.


Остановиться при первой неудаче

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

// src/config/validation.ts

const validationConfigurations = {
  rules: {
    [RequiredRule.ruleName]: RequiredRule,
    [StringRule.ruleName]: StringRule,
  },
  stopOnFirstFailure: true,
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь реализуем.

Перейти к rules-list.ts файл и обновить validate метод.


  /**
   * Validate the rules
   */
  public async validate() {
    for (const ruleName of this.rules) {
      // the rule will be in the rules section under the validation object
      const RuleClass = config.get(`validation.rules.${ruleName}`);

      const rule = new RuleClass(this.input, this.value);

      await rule.validate();

      if (rule.fails()) {
        this.errorsList.push(rule.error());

        // stop the loop after it fails
        if (config.get("validation.stopOnFirstFailure")) {
          break;
        }
      }
    }
  }
Войти в полноэкранный режим

Выйти из полноэкранного режима

Проверьте код внутри if утверждение, мы проверяем, stopOnFirstFailure верно, если это правда, мы разрываем петлю.


Стратегия ошибки возврата

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

// src/config/validation.ts

const validationConfigurations = {
  rules: {
    [RequiredRule.ruleName]: RequiredRule,
    [StringRule.ruleName]: StringRule,
  },
  stopOnFirstFailure: true,
  returnErrorStrategy: "first", // first or all
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь мы добавили новую конфигурацию под названием returnErrorStrategy и мы устанавливаем его на first.

Таким образом, мы можем выбирать между first а также all чтобы определить, как мы можем вернуть ошибку.

Пусть реализуют.

// src/validation/rules-list.ts

  /**
   * Get errors list
   */
  public errors() {
    if (config.get("validation.returnErrorStrategy") === "first") {
      return {
        input: this.input,
        errors: this.errorsList[0],
      };
    }

    return {
      input: this.input,
      errors: this.errorsList,
    };
  }
Войти в полноэкранный режим

Выйти из полноэкранного режима


Ключи ошибок

Мы также можем контролировать error keys что будет вернуться к ответу.

// src/config/validation.ts

const validationConfigurations = {
  rules: {
    [RequiredRule.ruleName]: RequiredRule,
    [StringRule.ruleName]: StringRule,
  },
  stopOnFirstFailure: true,
  returnErrorStrategy: "first", // first or all
  keys: {
    response: 'errors', // the key that will be used to return the entire errors
    inputKey: "input",
    inputErrors: "errors", // for all errors
    inputError: "error", // for first error
  },
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

Мы добавили новую конфигурацию под названием keys и мы устанавливаем response ключ к errors который будет использоваться для возврата всех ошибок.

inputKey будет использоваться для возврата ключа ввода.

inputErrors будет использоваться для возврата списка ошибок, если мы выберем all стратегия.

inputError будет использоваться для возврата первой ошибки, если мы выберем first стратегия.

Теперь реализуем.

// src/core/validator/rules-list.ts
  /**
   * Get errors list
   */
  public errors() {
    const responseKey = config.get("validation.keys.response");
    const inputKey = config.get("validation.keys.inputKey");
    const inputErrorsKey = config.get("validation.keys.inputErrors");
    const inputErrorKey = config.get("validation.keys.inputError");

    if (config.get("validation.returnErrorStrategy") === "first") {
      return {
        [responseKey]: {
          [inputKey]: this.input,
          [inputErrorKey]: this.errorsList[0],
        },
      };
    }

    return {
      [responseKey]: {
        [inputKey]: this.input,
        [inputErrorsKey]: this.errorsList,
      },
    };
  }
Войти в полноэкранный режим

Выйти из полноэкранного режима

Потрясающе, правда?

Теперь давайте обновим request.ts файл и обновить validate метод.

// src/core/http/request.ts

  /**
   * Execute the request
   */
  public async execute() {
    if (this.handler.validation) {
      const validator = new Validator(this, this.handler.validation.rules);

      await validator.scan(); // start scanning the rules

      if (validator.fails()) {
        const responseKey = config.get("validation.keys.response");
        return this.response.status(422).send({
          [responseKey]: validator.errors(),
        });
      }
    }

    return await this.handler(this, this.response);
  }
Войти в полноэкранный режим

Выйти из полноэкранного режима


Конфигурации по умолчанию

Это будет работать отлично, но если мы не забыли импортировать конфигурации, что, если мы забыли добавить конфигурации?

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

// src/core/validator/rules-list.ts

    /**
    * Get errors list
    */
    public errors() {
      const responseKey = config.get("validation.keys.response", "errors");
      const inputKey = config.get("validation.keys.inputKey", "input");
      const inputErrorsKey = config.get("validation.keys.inputErrors", "errors");
      const inputErrorKey = config.get("validation.keys.inputError", "error");

      if (config.get("validation.returnErrorStrategy", "first") === "first") {
        return {
          [responseKey]: {
            [inputKey]: this.input,
            [inputErrorKey]: this.errorsList[0],
          },
        };
      }

      return {
        [responseKey]: {
          [inputKey]: this.input,
          [inputErrorsKey]: this.errorsList,
        },
      };
    }
Войти в полноэкранный режим

Выйти из полноэкранного режима

// src/core/http/request.ts

  /**
   * Execute the request
   */
  public async execute() {
    if (this.handler.validation) {
      const validator = new Validator(this, this.handler.validation.rules);

      await validator.scan(); // start scanning the rules

      if (validator.fails()) {
        const responseKey = config.get("validation.keys.response", "errors");
        return this.response.status(422).send({
          [responseKey]: validator.errors(),
        });
      }
    }

    return await this.handler(this, this.response);
  }
Войти в полноэкранный режим

Выйти из полноэкранного режима


Конфигурация ошибки состояния ответа

Это мой недостаток, мы будем использовать статус 400, я писал его в предыдущих статьях как 422, что является другим кодом статуса.

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

// src/config/validation.ts

const validationConfigurations = {
  rules: {
    [RequiredRule.ruleName]: RequiredRule,
    [StringRule.ruleName]: StringRule,
  },
  stopOnFirstFailure: true,
  returnErrorStrategy: "first", // first or all
  keys: {
    response: "errors", // the key that will be used to return the entire errors
    inputKey: "input",
    inputErrors: "errors", // for all errors
    inputError: "error", // for first error
  },
  responseStatus: 400,
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

Теперь реализуем.

// src/core/http/request.ts

  /**
   * Execute the request
   */
  public async execute() {
    if (this.handler.validation) {
      const validator = new Validator(this, this.handler.validation.rules);

      await validator.scan(); // start scanning the rules

      if (validator.fails()) {
        const responseKey = config.get("validation.keys.response", "errors");
        return this.response
          .status(config.get("validation.responseStatus", 400))
          .send({
            [responseKey]: validator.errors(),
          });
      }
    }

    return await this.handler(this, this.response);
  }
Войти в полноэкранный режим

Выйти из полноэкранного режима

Вот и все!

Теперь у нас есть мощный класс проверки с множеством конфигураций.


🎨 Репозиторий проектов

Вы можете найти последние обновления этого проекта на Гитхаб


😍 Присоединяйтесь к нашему сообществу

Присоединяйтесь к нашему сообществу на Раздор для получения помощи и поддержки (канал Node Js 2023).


🎞️ Видеокурс (арабская озвучка)

Если вы хотите изучить этот курс в формате видео, вы можете найти его на YouTubeкурс на арабском языке.


💰 Бонусный контент 💰

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

Общие темы

Пакеты и библиотеки

Пакеты React Js

Курсы (Статьи)