Валидация форм средствами HTML5 – Блог ITVDN

Вступление

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

Правда с появлением HTML5 стало возможным указывать такой тип поля формы, как email, и браузер сам возьмет на себя его проверку. Такая возможность на данный момент реализована в Opera, так что расчитывать на нее пока особо не приходится. Поэтому я бы и хотел рассмотреть этот вопрос основательно.

В большинстве случаев валидация проводится так: каждому полю раздаётся id, и затем при submit’е вытаскиваем их, попутно проверяя содержимое. И всем хорош данный подход, кроме отсутствия в нем системности. Поэтому хочу предложить вашему вниманию свое решение данной проблемы.

Введение

Используя формы в HTML5, Вы часто используете методы проверки (валидации) данных, ведь их игнорирование может привести: к потерям пользователей,к мусору в базе данных или к взлому сайта. Исторически сложилось, что создание форм с хорошей валидацией – сложная задача.

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

Что такое валидация?

Валидация означает проверку данных, вводимых пользователем. В PHP доступны два типа проверки:

  • Валидация на стороне клиента — проверка выполняется на стороне клиента в веб-браузере.

  • Валидация на стороне сервера — после отправки данных на сервер их проверка осуществляется на серверной стороне.

Ниже приведена HTML-форма, которая содержит различные поля ввода: обязательные (required) и необязательные текстовые поля, переключатели (радио-кнопки) и кнопку отправки (submit). С этой формой мы будем с вами работать в этой главе. Попробуйте ввести данные:

Специализированные типы входных данных

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

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

  • color
  • date
  • datetime
  • datetime-local
  • email
  • month
  • number
  • range
  • search
  • tel
  • time
  • url
  • week

Пример:

Если браузер не поддерживает данный тип ввода, поле будет вести себя, как обычное поле ввода текста. 

Обязательные поля для заполнения

Просто добавив атрибут “required” к <input>, <select> или <textarea>. , Вы говорите браузеру, что значение должно быть заполнено.

Стилизирование

CSS3 псевдо-классы позволяют украсить форму в не зависимости от ее состояния. Это:

  • :valid
  • :invalid
  • :required
  • :optional
  • :in-range
  • :out-of-range
  • :read-only
  • :read-write

В примере мы объединили селекторы “valid” и “invalid” с псевдо-классом “focus” для закрашивания поля формы в красный или зеленый, в зависимости от того, что делает пользователь: выбирает или печатает.

 input:focus:invalid,

textarea:focus:invalid{

    border:solid2px#F5192F;

}

input:focus:valid,

textarea:focus:valid{

    border:solid2px#18E109;

    background-color:#fff;

}

Подсказки

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

Обратите внимание, что разные браузеры отображают всплывающие подсказки по-разному. В браузере Chrome значение названия атрибута будет отображаться мелким шрифтом, под основным сообщением об ошибке. В Firefox другая проблема: использовав атрибут “pattern” после того как он берется в качестве шаблона, Вы не получите всплывающую подсказку.

 <inputtype=”text”name=”name”title=”Пожалуйста введите имя пользователя.”>

Asp.net core | валидация пользователя

Последнее обновление: 15.12.2022

Кроме проверки пароля в ASP.NET Core Identity также валидируется и пользователь. В частности, если мы введем при регистрации некорректные данные для поля Email,
то получим ошибки валидации пользователя:

Валидация пользователя в ASP.NET Core

В данном случае ошибка говорит о том, что email некорректен и должен содержать только алфавитно-цифровые символы.

За валидацию пользователя по умолчанию отвечает класс UserOptions, который определяет следующие свойства:

Для применения этих свойств изменим установку контекста данных в методе ConfigureServices() в классе Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<User, IdentityRole>(opts => {
            opts.User.RequireUniqueEmail = true;	// уникальный email
            opts.User.AllowedUserNameCharacters = ".@abcdefghijklmnopqrstuvwxyz"; // допустимые символы
        })
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddControllersWithViews();
}

Свойство opts.User как раз и представляет класс UserOptions.

Этот способ работает, но все же он имеет недостатки:

UserOptions в ASP.NET Core

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

Класс валидатора должен реализовать интерфейс IUserValidator<T>:

public interface IUserValidator<TUser> where TUser : class {
	Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user);
}

Метод ValidateAsync() вызывается при валидации адреса электронной почты. В качестве параметров в метод передаются объект UserManager и
валидируемый пользователь.

Теперь создадим класс-валидатор CustomUserValidator:

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;

namespace CustomIdentityApp.Models
{
    public class CustomUserValidator : IUserValidator<User>
    {
        public Task<IdentityResult> ValidateAsync(UserManager<User> manager, User user)
        {
            List<IdentityError> errors = new List<IdentityError>();

            if (user.Email.ToLower().EndsWith("@spam.com"))
            {
                errors.Add(new IdentityError
                {
                    Description = "Данный домен находится в спам-базе. Выберите другой почтовый сервис"
                });
            }
            if (user.UserName.Contains("admin"))
            {
                errors.Add(new IdentityError
                {
                    Description = "Ник пользователя не должен содержать слово 'admin'"
                });
            }
            return Task.FromResult(errors.Count == 0 ?
                IdentityResult.Success : IdentityResult.Failed(errors.ToArray()));
        }
    }
}

Здесь проверяются пара условий: наличие слова “admin” и домен электронного адреса. В зависимости от результатов проверки добавляются соответствующие ошибки в
коллекцию errors. И если ошибок не будет найдено, что значит валидация прошла успешно, и возвращается значение IdentityResult.Success.

Теперь применим наш валидатор. Для этого добавим установку валидатора в метод ConfigureServices() класса Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IUserValidator<User>, CustomUserValidator>();

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<User, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddControllersWithViews();
}

И в этом случае, если мы введем некорректные значения, то увидим сообщения из нашего валидатора:

Создание валидатора пользователя в ASP.NET Core

Также для создания валидатора мы можем унаследовать свой класс от класса UserValidator:

public class CustomUserValidator : UserValidator<User>
{
    public override Task<IdentityResult> ValidateAsync(UserManager<User> manager, User user)
    {
        List<IdentityError> errors = new List<IdentityError>();

        if (user.Email.ToLower().EndsWith("@spam.com"))
        {
            errors.Add(new IdentityError
            {
                Description = "Данный домен находится в спам-базе. Выберите другой почтовый сервис"
            });
        }
        if (user.UserName.Contains("admin"))
        {
            errors.Add(new IdentityError
            {
                Description = "Ник пользователя не должен содержать слово 'admin'"
            });
        }
        return Task.FromResult(errors.Count == 0 ?
            IdentityResult.Success : IdentityResult.Failed(errors.ToArray()));
    }
}

Этот класс также реализует интерфейс IUserValidator, поэтому для его переопределения мы можем изменить метод ValidateAsync(). Результат будет тем же, что и в предыдущем случае.

НазадСодержаниеВперед

Livr — «независимые от языка правила валидации» или валидация данных без «проблем»

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

Основные проблемы, которые встречаются в библиотеках валидации данных

Проблема №1. Многие валидаторы проверяют только те данные, для которых описаны правила проверки. Для меня важно, чтобы любой пользовательский ввод, который явно не разрешен, был проигнорирован. То есть, валидатор должен вырезать все данные для которых не описаны правила валидации. Это просто фундаментально требование.

Проблема №2. Процедурное описание правил валидации. Я не хочу каждый раз думать про алгоритм валидации, я просто хочу описать декларативно, как должны выглядеть правильные данные. По сути, я хочу задать схему данных (почему не «JSON Schema» — в конце поста).

Проблема №3. Описание правил валидации в виде кода. Казалось бы, это не так страшно, но это сразу сводит на нет все попытки сериализации правил валидации и использования одних и тех же правил валидации на бекенде и фронтенде.

Проблема №4. Валидация останавливается на первом же поле с ошибкой. Такой подход не дает возможности подсветить сразу все ошибочные/обязательные поля в форме.

Проблема №5. Нестандартизированные сообщения об ошибках. Например, «Field name is required». Такую ошибку я не могу показать пользователю по ряду причин:

То есть, нужно возвращать не сообщение об ошибках, а стандартизированные коды ошибок.

Проблема №6. Числовые коды ошибок. Это просто неудобно в использовании. Я хочу, чтобы коды ошибок были интуитивно понятны. Согласитесь, что код ошибки «REQUIRED» понятней, чем код «27». Логика аналогична работе с классами исключений.

Проблема №7. Нет возможности проверять иерархические структуры данных. Сегодня, во времена разных JSON API, без этого просто не обойтись. Кроме самой валидации иерархических данных, нужно предусмотреть и возврат кодов ошибок для каждого поля.

Похожее:  Вход в личный кабинет налогоплательщика для физических лиц

Проблема №8. Ограниченный набор правил. Стандартных правил всегда не хватает. Валидатор должен быть расширяемый и позволять добавлять в него правила любой сложности.

Проблема №9. Слишком широкая сфера ответственности. Валидатор не должен генерировать формы, не должен генерировать код, не должен делать ничего, кроме валидации.

Проблема №10. Невозможность провести дополнительную обработку данных. Практически всегда, где есть валидация, есть необходимость в какой-то дополнительной (часто предварительной) обработке данных: вырезать запрещенные символы, привести в нижний регистр, удалить лишние пробелы. Особенно актуально — это удаление пробелов в начале и в конце строки. В 99% случаев они там не нужны. Я знаю, что я до этого говорил, что валидатор не должен делать ничего кроме валидации.

3 года назад, было решено написать валидатор, который не будет иметь всех вышеописанных проблем. Так появился LIVR (Language Independent Validation Rules). Есть реализации на Perl, PHP, JavaScript, Python (мы на python не пишем — фидбек по ней дать не могу). Валидатор используется в продакшене уже несколько лет практически в каждом проекте компании. Валидатор работает, как на сервере, так и на клиенте. Поиграться с валидатором можно тут — webbylab.github.io/livr-playground.

Ключевой идеей было то, что ядро валидатора должно быть минимальным, а вся логика валидации находится в правилах (вернее в их реализации). То есть, для валидатора нет разницы между правилами «required» (проверяет наличие значения), «max_length» (проверяет максимальную длину), «to_lc» (приводит данные в нижний регистра), «list_of_objects» (помогает описать правила для поля, которое содержит массив объектов).

Другими словами, валидатор ничего не знает ничего:

Все это ответственность правил валидации.

Спецификация LIVR

Поскольку задача стояла сделать валидатор независимым от языка программирования, этакий себе mustache/handlebars, но только в мире валидации данных, то начали мы с написания спецификации.

Цели спецификации:

  1. Стандартизировать формат описания данных.
  2. Описать минимальный набор правил валидации, которые должны поддерживаться каждой реализацией.
  3. Стандартизировать коды ошибок.
  4. Быть единой базовой документацией для всех реализаций.
  5. Иметь набор тестовых данных, которые позволяет проверить реализацию на соответствие спецификации

Спецификация доступна по адресу

livr-spec.org

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

Пример описания правил валидации для формы авторизации (демо):

{
    email: ['required', 'email'],
    password: 'required'
}

Пример правил валидации для регистрационной формы (

демо

):

{
    name: 'required',
    email: ['required', 'email'],
    gender: { one_of: ['male', 'female'] },
    phone: {max_length: 10},
    password: ['required', {min_length: 10} ]
    password2: { equal_to_field: 'password' }
}

Пример валидации вложенного объекта (

демо

):

{
    name: 'required',
    phone: {max_length: 10},
    address: { 'nested_object': {
        city: 'required',
        zip: ['required', 'positive_integer']
    }}
}

Правила валидации

Как описываются правила валиции? Каждое правило состоит из имени и аргументов (практически, как вызов функции) и в общем случае описывается следующим образом {«RULE_NAME»: ARRAY_OF_ARGUMENTS}. Для каждого поля описывается массив правил, которые применяются в порядке следования.

Например,

{
    "login": [ { length_between: [ 5, 10 ] } ]
}

То есть, у нас есть поле «login» и правило «length_between», которое имеет 2 аргумента ( «5» и «10» ). Это наиболее полная форма, но разрешены следующие упрощения

Все 3 записи идентичны:

"login": [ { required: [] } ]
"login": [ "required" ]
"login": "required"

Более детально расписано в спецификации в разделе «How it works».

Поддерживаемые правила

Все правила можно разделить на 3 глобальных группы:

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

Вот общий список правил, которые должны поддерживаться каждой реализаций валидатора:

Базовые правила

Правила для проверки строк

Правила для проверки чисел

Правила для специальных форматов

Правила для описания более сложных правил (метаправила)

Правила для преобразования данных (названия начинаются с глагола)

Метаправила

Пример и коды ошибок для каждого правила можно найти в LIVR-спецификации. Немного детальней остановимся лишь на метаправилах. Метаправила — это правила, которые позволяет скомбинировать простые правила в более сложные для валидации сложных иерархических структур данных. Важно понимать, что валидатор не делает различия между простыми правилами и метаправилами. Метаправила ничем не отличаются от того же “required” (да, я повторяюсь).

nested_object
Позволяет описывать правила валидации для вложенных объектов. Этим правилом вы будете пользоваться постоянно.
Код ошибки зависит от вложенных правил. Если вложенный объект не является хешом (словарем), то поле будет содержать ошибку: “FORMAT_ERROR”.
Пример использования (демо):

address: { 'nested_object': {
    city: 'required',
    zip: ['required', 'positive_integer']
}}

list_of

Позволяет описать правила валидации для списка значений. Каждое правило будет применяться для каждого элемента списка.

Код ошибки зависит от вложенных правил.

Пример использования (

демо

):

{ product_ids: { 'list_of': [ 'required',  'positive_integer'] }}

list_of_objects

Позволяет описать правила валидации для массива хешей(словарей). Похоже на «nested_object», но ожидает массив объектов. Правила применяются для каждого элемента в массиве.

Код ошибки зависит от вложенных правил. В случае если значение не является массивом, для поля будет возвращен код “FORMAT_ERROR”.

Пример использования (

демо

):

products: ['required', { 'list_of_objects': {
    product_id: ['required','positive_integer'],
    quantity: ['required', 'positive_integer']
}}]

list_of_different_objects

Аналогичен «list_of_objects», но бывает, что массив, который нам приходит, содержит объекты разного типа. Тип объекта мы можем определить по какому-то полю, например, «type». «list_of_different_objects» позволяет описать правила для списка объектов разного вида.

Код ошибки зависит от вложенных правил валидации. Если вложенных объект не является хешом, то поле будет содержать ошибку “FORMAT_ERROR”.

Пример использования (

демо

):

{
    products: ['required', { 'list_of_different_objects': [
        product_type, {
            material: {
                product_type: 'required',
                material_id: ['required', 'positive_integer'],
                quantity: ['required', {'min_number': 1} ],
                warehouse_id: 'positive_integer'
            },
            service: {
                product_type: 'required',
                name: ['required', {'max_length': 20} ]
            }
        }
    ]}]
}

В этом примере валидатор будут смотреть на “product_type” в каждом хеше и, в завимости от значения этого поля, будет использовать соответствующие правила валидации.

Формат ошибок

Как уже было сказано, правила возвращают строковые коды ошибок, которые понятны разработчику, например, «REQUIRED», «WRONG_EMAIL», «WRONG_DATE» и тд. Теперь разработчик может понять, в чем ошибка, осталось удобно донести в каких полях она возникла. Для этого валидатор возвращает структуру аналогичную переданной ему на валидацию, но она содержит только поля в которых возникли ошибки и вместо исходных значений в полях строковые коды ошибок.

Например, есть правила:

{
    name: 'required',
    phone: {max_length: 10},
    address: { 'nested_object': {
        city: 'required',
        zip: ['required', 'positive_integer']
    }}
}

и данные для валидации:

{
    phone: 12345678901,
    address: {
       city: 'NYC' 
    }
}

на выходе получим следующую ошибку

{
    "name": "REQUIRED",
    "phone": "TOO_LONG",
    "address": {
        "zip": "REQUIRED"
    }
}

демо валидации

REST API и формат ошибок

Возврат вменяемых ошибок всегда требует дополнительных усилий от разработчиков. И очень мало REST API, которые дают детальную информацию в ошибках. Часто это просто «Bad request» и все. Хочется, чтобы глядя на ошибку, к какому полю она относится и просто пути поля недостаточно, поскольку данные могут быть иерархическими и содержать массивы объектов… У нас в компании мы поступаем следующим образом — абсолютно для каждого запроса описываем правила валидации при помощи LIVR. В случае ошибки валидации, мы возвращаем объект ошибки клиенту. Объект ошибки содержит глобальный код ошибки и ошибку полученную от LIVR валидатора.

Например, вы передаете данные на сервер:

{
    "email": "user_at_mail_com",
    "age": 10,
    "address": {
        "country": "USQ"
    }
}

и в ответ получаете (

демо валидации на livr playground

):

{"error": {
    "code": "FORMAT_ERROR",
    "fields": {
        "email": "WRONG_EMAIL",
        "age": "TOO_LOW",
        "fname": "REQUIRED",
        "lname": "REQUIRED",
        "address":  {
            "country": "NOT_ALLOWED_VALUE",
            "city": "REQUIRED",
            "zip": "REQUIRED"
        }
    }
}}

Это значительно информативнее, чем какой-то «Bad request».

Работа с псевдонимами и регистрация собственных правил

Спецификация содержит только наиболее используемые правила, но у каждого проекта своя специфика и постоянно возникают ситуации, когда каких-то правил не хватает. В связи с этим, одним из ключевых требований к валидатору была возможность его расширения собственными правилами любого типа. Изначально каждая реализация имела свой механизм описания правил, но начиная со спецификации версии 0.4 мы ввели стандартный способ создания правил на базе других правил (создание псевдонимов), это покрывает 70% ситуаций. Рассмотрим оба варианта.

Похожее:  Истории любви на

Создание псевдонима
Способ, каким регистрируется псевдоним зависит от реализации, но то как псевдоним описывается — регламентировано спецификацией. Такой подход, например, позволяет сериализировать описания псевдонимов и использовать их с разными реализациями (например, на Perl-бекенде и JavaScript-фронтенде)

// Регистрация псевдонима "valid_address"
validator. registerAliasedRule({
    name: 'valid_address',
    rules: { nested_object: {
        country: 'required',
        city: 'required',
        zip: 'positive_integer'
    }}
});

// Регистрация псевдонима "adult_age"
validator.registerAliasedRule( {
    name: 'adult_age',
    rules: [ 'positive_integer', { min_number: 18 } ]
});

// Теперь псевдонимы доступны, как обычные правила.
{
    name: 'required',
    age: ['required', 'adult_age' ],
    address: ['required', 'valid_address']
}

Более того, можно устанавливать свои коды ошибок для правил.

Например,

validator.registerAliasedRule({
    name: 'valid_address',
    rules: { nested_object: {
        country: 'required',
        city: 'required',
        zip: 'positive_integer'
    }},
    error: 'WRONG_ADDRESS'
});

и в случае ошибки при валидации адреса, мы получим следующее:

{
    address: 'WRONG_ADDRESS'
}

Регистрация полноценного правила на примере JavaScript реализации

Для валидации используются функции обратного вызова, которые осуществляют проверку значений. Попробуем описать новое правило под названием “strong_password”. Будем проверять, что значение больше 8 символов и содержит цифры и буквы в верхнем и нижнем регистрах.

var LIVR = require('livr');

var rules = {password: ['required', 'strong_password']};

var validator = new LIVR.Validator(rules);

validator.registerRules({
    strong_password: function() {
        return function(val) {
            // пропускаем пустые значение. Для проверки на обязательность у нас и так есть правило "required"
            if (val === undefined || val === null || val === '' ) return;
            
            if ( length(val) < 8 || !val.match([0-9]) || !val.match([a-z] || !val.match([A-Z] ) ) {
                return 'WEAK_PASSWORD';
            }

            return;
          }
    }
});

Теперь добавим возможность задавать минимальное количество символов в пароле и зарегистрируем это правило как глобальное (доступное во всех экземплярах валидатора).

var LIVR = require('livr');

var rules = {password: ['required', {'strong_password': 10}]};

var validator = new LIVR.Validator(rules);

var strongPassword = function(minLength) {
    if (!minLength) throw "[minLength] parameter required";

    return function(val) {
        // пропускаем пустые значение. Для проверки на обязательность у нас и так есть правило "required"
        if (val === undefined || val === null || val === '' ) return;
            
        if ( length(val) < minLength || !val.match([0-9]) || !val.match([a-z] || !val.match([A-Z] ) ) {
            return 'WEAK_PASSWORD';
        }

        return;
    }
};

LIVR.Validator.registerDefaultRules({ strong_password: strongPassword });

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

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

Своя реализация по спецификации

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

По сути, каждый тест содержит несколько файлов:

Каждый негативный тест вместо «output.json» содержит «errors.json» с описанием ошибки, которая должна возникнуть в результате валидации. В тестах псевдонимов есть файл «aliases.json» с псевдонимами, которые необходимо предварительно зарегистрировать.

Почему не JSON Schema?

Часто задаваемый вопрос. Если коротко, то причин несколько:

JSON Schema содержит и интересные вещи, как-то возможность задать максимальное количество элементов в списке, но в LIVR это реализуется просто добавлением еще одного правила.

Ссылки по LIVR

UPD:

Вышел LIVR 2.0 (http://livr-spec.org/). С новых фич:

JavaScript реализация уже поддерживает все новые функции, остальные реализации в процессе обновления.

Атрибут type

Атрибут type определяет, какой тип ввода считается действительным для данного элемента. Если для атрибута type не указано значение, по умолчанию устанавливается тип text. Это в основном означает, что все виды вводимого текста будут считаться действительными для этого конкретного элемента.

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

email: Пользователю будет предложено ввести адрес электронной почты в корректном формате. Например, они не могут просто написать myemail.com или что-то@ или @кое-что. Им нужно будет ввести значение, аналогичное myemail@domain.tld. Конечно, они все равно могут вводить несуществующие адреса электронной почты, но это другая проблема!

number: Позволяет обеспечить, чтобы допустимыми являлись только цифры. Например, когда вы в форме спрашиваете кого-то о его возрасте, он не сможет предоставить данные в формате «картофель» или «тридцать шесть». Ему нужно будет написать фактическое число, например, 36 или 15.

url: Вы можете установить для атрибута type url, чтобы пользователи вводили действительный URL-адрес. В этом случае они не смогут ввести что-то вроде tutsplus. Кроме того, tutsplus.com также будет считаться недействительным — пользователям необходимо будет ввести полный URL-адрес, например //tutsplus.com.

tel: Использование этого значения не так полезно, как другие, потому что формат телефонного номера варьируется во всем мире. Просто нет стандартного шаблона, который браузеры могли бы сопоставить с элементом ввода, чтобы определить, является ли число действительным. Тем не менее, установка типа tel может быть полезна на более позднем этапе, когда вы выполняете собственную пользовательскую валидацию.

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

Следующая демо-версия на CodePen показывает, как мы можем использовать атрибут type для управления тем, что разрешено для различных полей ввода.

Атрибуты минимальной и максимальной длины

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

Необходимое значения для обоих этих атрибутов будут варьироваться в зависимости от конкретного случая. Например, некоторые веб-сайты могут требовать, чтобы имя пользователя было длиной от 4 до 15 символов, в то время как другие могут ограничивать максимальную длину до 12 символов. Аналогично, люди в некоторых странах будут иметь необычно короткие или длинные имена по сравнению с другими.

Валидация на клиенте и на сервере

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

Валидация форм средствами HTML5 - Блог ITVDN

Рис. 1. Общий алгоритм валидации данных

Даже если данные будут проверены на клиенте, на сервере они должны будут пройти повторную проверку.

Итак, проверка на стороне клиента хороша для ускорения обратной связи с клиентами. Однако она недостаточна, поскольку ее объем действия очень ограничен. Проверка выполняется только в пользовательском интерфейсе браузера. Проверка на стороне сервера является обязательной, поскольку проверка на стороне клиента не гарантирует, что на сервер не будут поступать неподтвержденные данные.

Валидация форм средствами HTML5 - Блог ITVDN

Рис. 2. Схема из презентации “Server vs Client-side validation.”, автор Arnold Sullivan

Часть контролов поддерживают внутреннюю валидацию. Например, Controls/date:BaseInput и Controls/dateRange:Input проверяют корректность введенных данных и показывают сообщение, если данные введены неправильно.

Такие контролы имеют внутри себя встроенный контейнер валидации и их можно не оборачивать внешним.

Валидаторы для Controls/date:BaseInput передаются во внутренний контейнер валидации с помощью опции valueValidators, которая содежит массив валидаторов. Валидаторы проверяют значение опции value.

У Controls/dateRange:Input есть две опции startValueValidators и endValueValidators, которые содержат массивы валидаторов. Валидаторы проверяют значение опций startValue и endValue соотвественно.

Виды валидации

В Wasaby существует три вида запуска валидации:

  • мгновенная;
  • при потере фокуса;
  • при отправке формы.

Чем раньше интерфейс сообщает об ошибке, тем лучше — пользователю проще вернуться и исправить ошибку.

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

Валидация при потере фокуса — основной вид запуска валидации. Валидация срабатывает сразу после потери фокуса, если значение в поле заполнено.

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

Похожее:  laravel-docs-8.x-ru/validation.md at main · avsbru/laravel-docs-8.x-ru · GitHub

Использование регулярных выражений для валидации формы

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

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

Вот несколько примеров использования регулярных выражений с атрибутом pattern.

Приведенный выше шаблон будет проверять, чтобы все имена пользователей содержали только символы от a до z, от A до Z или от 0 до 9. Например, monty42, 42monty, MON42ty и mon42ty являются допустимыми именами пользователей, а monty_42 — нет.

Атрибуты minlength и maxlength помогут убедиться, что имя пользователя не является слишком коротким или слишком длинным.

Если вы хотите, чтобы имя пользователя начиналось с определенного символа, например подчеркивания, вы можете просто добавить его в начало шаблона.

Теперь каждое имя пользователя, которое не начинается с _ и содержит какие-либо символы, кроме a-z, A-Z или 0-9, будет считаться недействительным.

Я надеюсь, теперь вы понимаете, как мы можем использовать атрибут pattern и регулярные выражения, чтобы ограничить то, что считается допустимым вводом, даже когда для атрибута type установлено значение text.

Как избежать эксплойтов $_server[“php_self”]?

Экспло́йт (англ. exploit, эксплуатировать) — это фрагмент вредоносного программного кода либо последовательность команд, которые используют уязвимости в программе и применяеются для проведения хакерской атаки.

Чтобы избежать экспойты нужно переменную $_SERVER[“PHP_SELF”] предать в качестве аргумента в функцию htmlspecialchars():

Обязательные поля и заполнитель текста

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

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

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

Остренькое

1

. Если мы зануляем

single

, то не уничтожим ли мы попутно и

Singleton.fields

? Нет. И вот почему. Присваивая переменной

fields

объект мы не передаем сам объект, а лишь ссылку на него. Физически объект хранится в так называемой

Memory Heap

. И не будет удален оттуда garbage collector’ом до тех пор, пока есть хотя бы одна ссылка на него. Таким образом мы лишь удалили ссылку на массив, и объекты не были уничтожены, т.к. существует еще одна ссылка на них, а именно

Singleton.fields

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

3. Почему когда содержимое поля удовлетворяет регулярному выражению я делаю проверку еще раз? На это вразумительного ответа у меня нет. Опытным путем я заметил, что когда применяется функция RegExp.test(), то сначала она возвращает результат ожидаемый, а потом прямо противоположный.

Послесловие


Итак с упрощенной задачей проверки содержимого формы мы справились. Однако остались следующие вопросы:

Как задать произвольный стиль для некорректно заполненного поля?

Как добавить валидацию на другие события, например, когда проверка поля нужна по мере ввода данных?


Как выдавать сообщение, которое бы помогло пользователю понять в чем ошибка?

Что делать, если форм на странице больше одной?

Эти вопросы я постараюсь рассмотреть во второй части поста.

Примечание о безопасности форм php

Учтите, что переменная $_SERVER [“PHP_SELF”] может использоваться хакерами!

Если Вы используете на странице сайта PHP_SELF, то пользователь может ввести в адресной строке косую черту (/), а затем выполнить несколько команд межсайтового скриптинга (XSS).

Предположим, у нас есть следующая форма на странице с именем “send_form.php”:

Проверка данных формы с помощью php

Первым делом передадим все переменные формы в функцию PHP htmlspecialchars().
Эта функция возвращает строку, над которой проведены рассмотренные выше преобразования. Этих преобразований достаточно для защиты от эксплойта.

Теперь код можно безопасно отображать на странице или в электронном письме.

Сдедующим шагом применим ещё две функции:

  1. Из данных, вводимых пользователем (с помощью PHP функции trim()) удалим ненужные символы (лишние пробелы, табуляции, переходы на новую строку)
  2. C помощью PHP функции stripslashes() из данных, вводимых пользователем, удалим обратную косую черту ()

И, наконец, создадим функцию, которая будет выполнять все рассмтренные проверки за нас, что намного удобнее, чем писать один и тот же код снова и снова. Назовем функцию test_input().

В следующем примере будем проверять каждую переменную $_POST с помощью функции test_input():

В начале сценария мы проверяем, используя суперглобальную переменную $ _SERVER [“REQUEST_METHOD”], была ли отправлена ​​форма. Если для запроса страницы был использован метод POST, значит, форма была отправлена ​​— и ее нужно проверить. Если форма не была отправлена — пропускаем проверку и отображаем пустую форму.

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

В следующем уроке сделаем поля ввода обязательными, а также разберёмся как, при необходимости, создать сообщения об ошибках.

Радио-кнопки

В нашей форме выбор образования осуществляется с помощью элементов <input> типа radio (переключатели), которые используют принцип логического «ИЛИ», позволяя выбрать только одно из нескольких значений: если вы выбираете одно положение, то все остальные становятся неактивными:

Текстовые поля

Поля для ввода имени, адреса электронной почты и веб-сайта создается с помощью элемента <input> (от англ. input — ввод) с атрибутом type=”text”, а поле для комментария применяется элемент <textarea> (текстовая область). HTML-код выглядит так:

Цимес


Данный скрипт обладает большой гибкостью в том смысле, что при переносе придется лишь изменить имена полей (ключи массива

single

) и регулярные выражения для их проверки.

Посмотреть работу скрипта можно здесь.

Элемент ввода формы

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

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

Обе эти проблемы можно легко решить, используя с элементами формы некоторые атрибуты HTML5.

Заключение

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

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

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 5,00 из 5)
Загрузка...

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

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

Adblock
detector