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

Основные принципы валидации

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

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

Правильное сочетание этих подходов позволяет создавать устойчивые и удобные интерфейсы.


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

В Mithril.js клиентская валидация строится на принципах реактивного обновления данных через m.stream или локальные состояния компонентов.

Пример структуры данных формы:

import m from "mithril";

const formData = {
    username: "",
    email: "",
    password: ""
};

const errors = {
    username: "",
    email: "",
    password: ""
};

Реализация функций проверки:

function validateUsername(value) {
    if (!value) return "Имя пользователя обязательно";
    if (value.length < 3) return "Имя должно содержать минимум 3 символа";
    return "";
}

function validateEmail(value) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!value) return "Email обязателен";
    if (!emailRegex.test(value)) return "Неверный формат email";
    return "";
}

function validatePassword(value) {
    if (!value) return "Пароль обязателен";
    if (value.length < 6) return "Пароль должен содержать минимум 6 символов";
    return "";
}

Применение в компоненте Mithril:

const FormComponent = {
    view: () => m("form", {
        onsubmit: (e) => {
            e.preventDefault();
            errors.username = validateUsername(formData.username);
            errors.email = validateEmail(formData.email);
            errors.password = validatePassword(formData.password);

            if (!errors.username && !errors.email && !errors.password) {
                // Отправка данных на сервер
                console.log("Форма валидна, отправка...");
            }
        }
    }, [
        m("input[type=text][placeholder=Username]", {
            oninput: e => formData.username = e.target.value,
            value: formData.username
        }),
        errors.username && m("div.error", errors.username),
        
        m("input[type=email][placeholder=Email]", {
            oninput: e => formData.email = e.target.value,
            value: formData.email
        }),
        errors.email && m("div.error", errors.email),
        
        m("input[type=password][placeholder=Password]", {
            oninput: e => formData.password = e.target.value,
            value: formData.password
        }),
        errors.password && m("div.error", errors.password),
        
        m("button[type=submit]", "Отправить")
    ])
};

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


Серверная валидация

Серверная валидация реализуется в API и является последней линией защиты. Даже если клиент прошёл валидацию, сервер должен проверять корректность данных. В Node.js, например, это может быть реализовано с использованием Express.js:

import express from "express";
import bodyParser from "body-parser";

const app = express();
app.use(bodyParser.json());

function validateServerData(data) {
    const errors = {};
    
    if (!data.username || data.username.length < 3) {
        errors.username = "Имя пользователя должно содержать минимум 3 символа";
    }
    
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!data.email || !emailRegex.test(data.email)) {
        errors.email = "Неверный формат email";
    }
    
    if (!data.password || data.password.length < 6) {
        errors.password = "Пароль должен содержать минимум 6 символов";
    }
    
    return errors;
}

app.post("/register", (req, res) => {
    const errors = validateServerData(req.body);
    if (Object.keys(errors).length) {
        return res.status(400).json({ errors });
    }
    res.status(200).json({ message: "Регистрация успешна" });
});

app.listen(3000);

Сервер возвращает объект с ошибками, который можно отобразить на клиенте, используя структуру, аналогичную локальным errors в компоненте Mithril.


Интеграция клиентской и серверной валидации

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

Пример обработки ошибок сервера в Mithril:

function submitForm() {
    errors.username = "";
    errors.email = "";
    errors.password = "";

    m.request({
        method: "POST",
        url: "/register",
        body: formData
    })
    .then(response => console.log(response.message))
    .catch(err => {
        if (err.response && err.response.errors) {
            Object.assign(errors, err.response.errors);
        }
    });
}

Асинхронная валидация

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

async function checkEmailUnique(email) {
    const response = await m.request({
        method: "POST",
        url: "/check-email",
        body: { email }
    });
    return response.exists ? "Email уже используется" : "";
}

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


Рекомендации по организации кода

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

Валидация в Mithril.js строится на принципах модульности и реактивности. Использование структуры formData и errors позволяет гибко управлять состоянием формы, обеспечивая мгновенную обратную связь на клиенте и надежную проверку на сервере. Асинхронные проверки и интеграция с API делают форму полноценным инструментом для безопасного и корректного ввода данных.