Web разработка. Когда использовать JavaScript библиотеки для проверки форм

Владимир | | Ajax, CodeIgniter, HTML, JavaScript.

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

В случае web приложений ситуация только усложняется.

На мой взгляд, существует три основных источника проблем:
1) ошибки пользователя (тут вряд ли можно что-то сделать);
2) необходимость проверять данные на стороне сервера;
3) необходимость сообщать посетителю об ошибках без перезагрузки страницы.

Кроме того, существуют два способа отправки данных формы:
1) обычный (с перезагрузкой страницы);
2) асинхронный (ajax запрос, без перезагрузки страницы).

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

Например. jQuery Validation Plugin, о нем, кстати, написана очень хорошая статья Проверка данных форм.
Для prototype тоже есть несколько аналогичных библиотек: Dexagogo и JSValidate.

Принцип их работы примерно одинаков. Создаете форму с правилами (обычно они указываются в атрибутах class полей формы), подключаете библиотеку, если нужно, пишите свои собственные правила.

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

Теперь посмотрите на диаграммы отправки данных для обычного и ajax запросов.

Отправка обычного запроса (миниатюра) Отправка ajax запроса (миниатюра)

Как видите, все недостатки налицо. Самое главное – дублируется код проверки данных на стороне браузера и сервера, причем на 2-х разных языках.

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

Посмотрим, что можно сделать.

Убирать проверку на стороне сервера, мягко говоря, не безопасно.

А вот на клиентской стороне ситуация интереснее.

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

А вот в случае ajax запроса перезагрузки страницы не происходит. Сервер отправляет только результат обработки запроса (либо описания ошибок).

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

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

Теперь приведу пример небольшой формы, которая выполняет ajax запрос.

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

Серверная часть написана на PHP с использованием фреймворка CodeIgniter.

Клиентская – JavaScript с использованием библиотеки prototype (версия должна быть не ниже 1.6.0).

Рассмотрим контроллер (main.php).

Примечание. Для тех, кто не знаком с CodeIgniter, поясню. При обращении к сайту будет вызван один из методов контроллера (по-умолчанию, index()). Подробнее о взаимодействии компонентов фреймворка можно почитать здесь.

class Main extends Controller {
	function Main() {
		parent::Controller();
	}
	function index() {
		//загружаем необходимые библиотеки и страницу с формой
		$this->load->helper('form');
		$this->load->helper('url');
		$this->load->view('mainform');
	}
	/**
	 * Этот метод обрабатывает ajax запрос с данными формы
	 */
	function processform() {
		$this->load->library('validation');

		$rules['field1'] = "required";
		$rules['field2'] = "required|min_length[5]";

		$fields['field1'] = "Поле 1";
		$fields['field2'] = "Поле 2";

		$this->validation->set_rules($rules);
		$this->validation->set_fields($fields);
		$this->validation->set_error_delimiters('', '');

		$res = array();
		if ($this->validation->run()) {
			$res['status'] = "OK";
		}
		else {
			$res['status'] = "ERR";
			$res['errMessages']['field1'] = $this->validation->field1_error;
			$res['errMessages']['field2'] = $this->validation->field2_error;
		}

		echo json_encode($res);
	}
}

Как видите, контроллер содержит два метода:
1) index() – загружает библиотеки и представление, создающее страницу с формой;
2) processform() – обрабатывает ajax запрос. Этот метод содержит обычный код проверки формы.
Сначала загружается библиотека validation (строка 15). Затем, устанавливаются правила и описания для каждого поля формы (строки 17-24). Обратите внимание, ключи элементов массива должны совпадать с id полей формы.

В строке 25 мы указываем, что строки с описанием ошибок не должны содержать html разметки (по-умолчанию, библиотека вставляет теги <p>).

После этого выполняем проверку (строка 28).

В зависимости от результатов проверки формируем массив, в котором в обязательном порядке должны быть элементы:
1) status – результат проверки (может быть OK или ERR);
2) errMessages – создается если status == 'ERR'. Этот параметр содержит массив с описаниями ошибок. Ключи массива должны совпадать с именами полей формы (нужно для автоматической обработки массива на стороне браузера).

После того, как массив сформирован, преобразуем его в формат JSON и отправляем браузеру.

Теперь рассмотрим представление, создающее форму (mainform.php).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ru">
<head>
<title>Проверка данных форм</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<script type="text/javascript" src="<?php echo base_url(); ?>prototype.js"></script>
<script type="text/javascript" src="<?php echo base_url(); ?>script.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>

<body>
<?php echo form_open('#'); ?>
<p>
<label for="field1">Поле 1: </label>
<input type="text" name="field1" id="field1" size="15" />
</p>
<p>
<label for="field2">Поле 2: </label>
<input type="text" name="field2" id="field2" size="15" />
</p>
<p>
<input type="button" value="Отправить" onclick="sendData('<?php echo site_url()."/main/processform"; ?>')" />
</p>
<?php echo form_close(); ?>
</body>
</html>

Разметка страницы тут принципиальной роли не играет. Главное, подключить необходимые библиотеки (строки 6 и 7) и правильно указать адрес обработчика запроса в параметре функции sendData (строка 22).

И, наконец, переходим к функции sendData (находится в файле script.js).

var fields = ["field1", "field2"];

function sendData(scriptUrl) {
	//читаем данные из формы
	//формируем строку с параметрами запроса
	var pars = $H();
	for (var i = 0; i < fields.length; i++) {
		pars.set(fields[i], $(fields[i]).value);
	}
	//отправляем ajax запрос
	new Ajax.Request(scriptUrl,
	{
		method:"post",
		parameters:pars.toQueryString(),
		onSuccess: function(transport) {
			var response = eval('(' + transport.responseText + ')');
			//удаляем старые сообщения об ошибках
			var errMess = $$('.error');
			errMess.each(
				function(el) {
					el.remove();
				}
			);
			if (response.status == "OK") {
				//код, который выполняется если форма заполнена правильно
				alert("ОК");
			}
			else {
				//выводим сообщения об ошибках
				var messages = response.errMessages;
				for (var i = 0; i < fields.length; i++) {
					var curField = fields[i];
					if (messages[curField] != null && messages[curField] != "") {
						new Insertion.After(curField,
							"<span class=\"error\">" + messages[curField] + "</span>");
					}
				}
			}
		},
		onFailure: function() {
			alert("При отправке запроса возникла ошибка");
		}
	});
}

Здесь выполняется чтение значений, введенных в форму (строки 6-9), отправка запроса (строка 11) и обработка его результатов (строки 15-39).

Если форма заполнена правильно, функция покажет сообщение со словом «ОК» (строка 26). Естественно, в реальной ситуации этот код нужно изменить.

А в случае возникновения ошибок – после каждого поля, содержащего неправильные значения, будет вставлена строка с описанием ошибки. Каждая такая строка находится между тегами <span class=”error”>...</span>, т.е. с помощью таблицы стилей эти ошибки легко выделить.

Обратите внимание. Функция sendData работает с любым количеством полей в форме. Нужно только добавить id этих полей в массив fields (строка 1).

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

1) Изменить html разметку (добавить новые поля в форму), каждое поле должно иметь уникальный id.
2) добавить id полей в массив fields файл (script.js).
3) в методе processform() контроллера создаем правила для каждого поля и в массив $res['errMessages'] добавляем элементы, в которых будут храниться описания ошибок.

Вот и все. Главное – не ошибиться с id полей формы.

В качестве дополнения, приведу таблицу стилей (styles.css) для этой формы.

@CHARSET "UTF-8";

form {
	font-family: Verdana, sans-serif;
	font-size: 90%;
}
form p {
	clear: both;
}
form label {
	float: left;
	width: 20%;
}
.error {
	margin-left: 10px;
	color: #ff0000;
	font-size: 90%;
}

До встеречи!

P.S. А каким образом вы решаете проблемы с проверкой данных?

  • MAX

    Что делать пользователям с php < 5.2? Вроде там json_encode еще не было…

    • Использовать PEAR::JSON, вестимо.

    • Еще один вариант. Формат JSON достаточно простой и не сложно сформировать строку вручную.

      Например,
      {«state»:»ERR»,»errMessages»:{«titleErr»:»…»}}

      Если сложные структуры данных не передаются, то проблем нет.

      Опять же, JSON — не единственный вариант. Можно использовать XML.

  • MAX

    Что делать пользователям с php < 5.2? Вроде там json_encode еще не было…

    • Использовать PEAR::JSON, вестимо.

    • Еще один вариант. Формат JSON достаточно простой и не сложно сформировать строку вручную.

      Например,
      {«state»:»ERR»,»errMessages»:{«titleErr»:»…»}}

      Если сложные структуры данных не передаются, то проблем нет.

      Опять же, JSON — не единственный вариант. Можно использовать XML.

  • Автор, спасибо за полезные статьи.

  • Автор, спасибо за полезные статьи.

  • madhun

    Имхо, полагаться в валидации формы только на Ajax — неверно. Пользователь всегда может отключить JavaScript в браузере (или, что еще вероятней, зайдет с устаревшим браузером) — в итоге валидация вообще не будет работать, а форма будет отправляться.
    Вариантов тут два:
    — Делать проверку и на клиентской стороне, и на серверной (дублирование, но надежно),
    — Делать клиентскую проверку через Ajax + серверную, пользуя тот же код (метод).

    • Все дело в том, что при отключенном JS (или старом браузере) форма отправляться вообще не будет — у нее submit отсутствует.
      Кроме этого, валдидация на AJAX — это не валидация джаваскриптом. Это обычная серверная валидация, просто без перегрузки страницы.

      • madhun

        А как по вашему будет работать AJAX при отключенном JavaScript-е? Ни один из известных мне AJAX-транспортов не работает без него. Да даже если бы работал — через что вы бы получали и изменяли данные на странице?
        Также получается что при отключенном JavaScript форма вообще не будет работать. Кстати, а по Enter-е ведь ее все равно можно будет отправить?

        • AJAX работать при отключенном JS однозначно не будет.
          Кроме того, если пользователь отключил JS в браузере, то проверка возможна только на сервере, и тут возможен только один вариант — обычная форма с submit (никакого AJAX и проверок на стороне браузера).
          Повторюсь, убирать проверку на стороне сервера нельзя, получите «дыру» в безопасности.

        • В примере к этой статье при отключенном JavaScript форму отправить нельзя. Т.к. отправка выполняется js-функцией sendData (строка 22, листинг 2). Нажатие на enter просто вызывает эту функцию.

        • madhun

          Кстати, в этой форме (к сожалению, тут конечного HTML-кода не приведено) событие повешано на onclick кнопки button — это не сабмит. Вообще валидацию формы надо вешать, по-хорошему, на событие onsubmit самой формы — это самое надежное. Ведь в случае с этой кнопкой типа button форма может отправляться по Enter в обход её (она же не submit). К тому же это еще вопрос, как разные браузеры работают с событием onclick (и ему подобным) в случае с отключенным JS. Если уж стопудова блокировать форму — то onsubmit=»return false».

        • В принципе, согласен, наверное лучше использовать submit.
          Но в данном случае запрос не может быть отправлен в обход проверки, т.к. открывающий тег формы имеет вид:
          <form action=»#» method=»post»>
          (строка 12, листинг 2, там используется функция form_open(‘#’) из библиотеки CI).
          Проверка выполняется сервером при обработке ajax запроса, поэтому отправить форму в обход JS можно только если сформировать запрос вне браузера (например, с помощью cURL). А в результате — получите строку с описаниями ошибок.

        • А что гадать на кофейной гуще: «че будет, …че не будет??» Наверное не очень сложно проанализировать включен JS или нет и выдать клиенту в ту же форму сообщение: «Ваш броузер не поддерживает JavaScript! …» и уйти программно с проверкой на сервер — защита от дурака или подобной ситуации актуальна. Это мое дилетантское мнение

        • Проверить включен JavaScript или нет не сложно. Но тема статьи «Когда использовать JavaScript библиотеки…», т.е. предполагается, что он включен 🙂

        • Mr. X

          Тут видимо опасность в другом, сразу извинюсь, если чего-то не понял, опираясь на Ajax мы опираемся на работу javascript на стороне клиента. Верно? Например,в общем случае вы сделали проверку кода на стороне клиента, браузер читает html-код, доходит до скрипта и начинает интерпретацию. Но тут можно на стороне клиента много чего изменить, можно изменить саму интерпретацию, возьмите простой движок webkit.org и самый простой браузер на его основе http://code.google.com/p/arora/. Есть вероятность подмены javascript кода на этапе передачи в webkit(заголовочные файлы webpage.h и другие), потом скомпилировать это… я пробую это сделать, пока правда ничего не работает, но я на 100% уверен, что дело времени. по — моему я даже видел похожие эксплойты где-то…

        • Здесь речь не о защите сайта от атак. Реализовать ее можно только на стороне сервера, т.к. кто-угодно может отправить запрос с любыми данными используя тот же cURL.

          Проверка на стороне клиента нужна исключительно для удобства посетителей. Чтобы они видели ошибки сразу, а не после отправки запроса и перезагрузки страницы. Если эти пользователи начинают менять страницу — это их проблемы. Все равно серверный (php) сам выполнит все проверки.

  • madhun

    Имхо, полагаться в валидации формы только на Ajax — неверно. Пользователь всегда может отключить JavaScript в браузере (или, что еще вероятней, зайдет с устаревшим браузером) — в итоге валидация вообще не будет работать, а форма будет отправляться.
    Вариантов тут два:
    — Делать проверку и на клиентской стороне, и на серверной (дублирование, но надежно),
    — Делать клиентскую проверку через Ajax + серверную, пользуя тот же код (метод).

    • Все дело в том, что при отключенном JS (или старом браузере) форма отправляться вообще не будет — у нее submit отсутствует.
      Кроме этого, валдидация на AJAX — это не валидация джаваскриптом. Это обычная серверная валидация, просто без перегрузки страницы.

      • madhun

        А как по вашему будет работать AJAX при отключенном JavaScript-е? Ни один из известных мне AJAX-транспортов не работает без него. Да даже если бы работал — через что вы бы получали и изменяли данные на странице?
        Также получается что при отключенном JavaScript форма вообще не будет работать. Кстати, а по Enter-е ведь ее все равно можно будет отправить?

        • AJAX работать при отключенном JS однозначно не будет.
          Кроме того, если пользователь отключил JS в браузере, то проверка возможна только на сервере, и тут возможен только один вариант — обычная форма с submit (никакого AJAX и проверок на стороне браузера).
          Повторюсь, убирать проверку на стороне сервера нельзя, получите «дыру» в безопасности.

        • В примере к этой статье при отключенном JavaScript форму отправить нельзя. Т.к. отправка выполняется js-функцией sendData (строка 22, листинг 2). Нажатие на enter просто вызывает эту функцию.

        • madhun

          Кстати, в этой форме (к сожалению, тут конечного HTML-кода не приведено) событие повешано на onclick кнопки button — это не сабмит. Вообще валидацию формы надо вешать, по-хорошему, на событие onsubmit самой формы — это самое надежное. Ведь в случае с этой кнопкой типа button форма может отправляться по Enter в обход её (она же не submit). К тому же это еще вопрос, как разные браузеры работают с событием onclick (и ему подобным) в случае с отключенным JS. Если уж стопудова блокировать форму — то onsubmit=»return false».

        • В принципе, согласен, наверное лучше использовать submit.
          Но в данном случае запрос не может быть отправлен в обход проверки, т.к. открывающий тег формы имеет вид:
          <form action=»#» method=»post»>
          (строка 12, листинг 2, там используется функция form_open(‘#’) из библиотеки CI).
          Проверка выполняется сервером при обработке ajax запроса, поэтому отправить форму в обход JS можно только если сформировать запрос вне браузера (например, с помощью cURL). А в результате — получите строку с описаниями ошибок.

        • А что гадать на кофейной гуще: «че будет, …че не будет??» Наверное не очень сложно проанализировать включен JS или нет и выдать клиенту в ту же форму сообщение: «Ваш броузер не поддерживает JavaScript! …» и уйти программно с проверкой на сервер — защита от дурака или подобной ситуации актуальна. Это мое дилетантское мнение

        • Проверить включен JavaScript или нет не сложно. Но тема статьи «Когда использовать JavaScript библиотеки…», т.е. предполагается, что он включен 🙂

        • Mr. X

          Тут видимо опасность в другом, сразу извинюсь, если чего-то не понял, опираясь на Ajax мы опираемся на работу javascript на стороне клиента. Верно? Например,в общем случае вы сделали проверку кода на стороне клиента, браузер читает html-код, доходит до скрипта и начинает интерпретацию. Но тут можно на стороне клиента много чего изменить, можно изменить саму интерпретацию, возьмите простой движок webkit.org и самый простой браузер на его основе http://code.google.com/p/arora/. Есть вероятность подмены javascript кода на этапе передачи в webkit(заголовочные файлы webpage.h и другие), потом скомпилировать это… я пробую это сделать, пока правда ничего не работает, но я на 100% уверен, что дело времени. по — моему я даже видел похожие эксплойты где-то…

        • Здесь речь не о защите сайта от атак. Реализовать ее можно только на стороне сервера, т.к. кто-угодно может отправить запрос с любыми данными используя тот же cURL.

          Проверка на стороне клиента нужна исключительно для удобства посетителей. Чтобы они видели ошибки сразу, а не после отправки запроса и перезагрузки страницы. Если эти пользователи начинают менять страницу — это их проблемы. Все равно серверный (php) сам выполнит все проверки.

  • Pingback:   Интересно почитать (04.04.2008) by Блог Димка()

  • m

  • m

  • Жаль, что Вы не указали основную мотивацию к проверке и формированию подобного рода запроса к Базе (читай серверу) на стороне клиента. Ведь совершенно ясно, что уменьшение нагрузки на сервер тысячами мелочных запросов однозначно благоприятствует работе размещенного на нем ресурса. Поэтому, наверное, все, что может быть отдано для обработки клиенту должно быть ему передано. Что-то типа «распределенных вычислений» — пусть сам пользователь на своей машине выполняет максимальную часть «вычислительной работы»…
    Многие программисты так увлекаются «кодированием в РНР», что и графику в нем обрабатывают. Этот пост и Ваш же пример работы с Flash http://www.simplecoding.org/open-flash-chart-stroim-grafiki.html — говорит о необходимости правильного распределения ресурсов при решении разных задач в рамках проекта

    • Распределять правильно ресурсы, конечно, нужно, но убирать проверки нельзя.
      Ситуация такая. Проверка на стороне сервера обязательна в любом случае, т.к. от клиента может прийти что угодно. Используя, например, curl можно отправить любой запрос любому серверу и с любыми данными. Поэтому проверки на стороне клиента с точки зрения безопасности ничего не дают. Но, с другой стороны, они увеличивают удобство работы со страницей. Т.к. посетителю не нужно ждать ответ сервера с сообщением о том, что он забыл какое-то поле в форме заполнить.

    • По поводу графики. Есть разные ситуации. Например, если вы создаете captcha, то картинка должна быть создана сервером.
      Или другой пример. Я пользуюсь online блокнотом (evernote), в нем после сохранения записи создается её графическая превьюшка. Кстати, очень помогает ориентироваться по записям, особенно, если в них вставлены картинки.

  • Жаль, что Вы не указали основную мотивацию к проверке и формированию подобного рода запроса к Базе (читай серверу) на стороне клиента. Ведь совершенно ясно, что уменьшение нагрузки на сервер тысячами мелочных запросов однозначно благоприятствует работе размещенного на нем ресурса. Поэтому, наверное, все, что может быть отдано для обработки клиенту должно быть ему передано. Что-то типа «распределенных вычислений» — пусть сам пользователь на своей машине выполняет максимальную часть «вычислительной работы»…
    Многие программисты так увлекаются «кодированием в РНР», что и графику в нем обрабатывают. Этот пост и Ваш же пример работы с Flash http://www.simplecoding.org/open-flash-chart-stroim-grafiki.html — говорит о необходимости правильного распределения ресурсов при решении разных задач в рамках проекта

    • Распределять правильно ресурсы, конечно, нужно, но убирать проверки нельзя.
      Ситуация такая. Проверка на стороне сервера обязательна в любом случае, т.к. от клиента может прийти что угодно. Используя, например, curl можно отправить любой запрос любому серверу и с любыми данными. Поэтому проверки на стороне клиента с точки зрения безопасности ничего не дают. Но, с другой стороны, они увеличивают удобство работы со страницей. Т.к. посетителю не нужно ждать ответ сервера с сообщением о том, что он забыл какое-то поле в форме заполнить.

    • По поводу графики. Есть разные ситуации. Например, если вы создаете captcha, то картинка должна быть создана сервером.
      Или другой пример. Я пользуюсь online блокнотом (evernote), в нем после сохранения записи создается её графическая превьюшка. Кстати, очень помогает ориентироваться по записям, особенно, если в них вставлены картинки.

  • Руся

    Как вы делаете такие логические схемы? какой программой?

    • Эти схемы называются UML-диаграммами. Делал я их с помощью StarUML, но, к сожалению, эта программа давно не развивается. И есть проблемы в работе на Win7. Сейчас сам ищу хорошую альтернативу.