Яндекс.Метрика

    Песочница

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

    Работая над написанием административного, да и пользовательского интерфейса, я не раз ловил себя на мысли — а все ли я делаю так, чтобы при минимуме усилий обеспечить оптимальное качество?

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

    Программирую я на PHP, поэтому примеры серверного кода будут на этом языке.

    Представим, что мы решили дополнить свой проект формой для заказа обратного звонка. Мало ли — линии часто перегружены, клиент хочет почувствовать свою весомость, мода такая… ну в общем надо. Быстро накидываем простейший html, пишем обработчик — данные красиво легли в базу, в общем — практически готово. Осталось сделать самую малость — написать обработку ошибок. (Конечно, порядок разработки весьма условен). Каким образом можно это проверить введенную пользователем информацию на правильность и уведомить его о результате?

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

    Рассмотрим базовую обработку ошибок (считаем что пользовательские данные уже прошли основные проверки и фильтрации):

    <?php
    
    $errors = array();
    
    if ( empty($person) || empty($phone) || empty($question) )
    {
    	$errors[] = 'Не заполнены обязательные поля';
    }
    
    if ( count($errors) )
    {
    	$template->assign_switch('errors', 1);
    
    	$template->vars(array(
    		'ERRORS' => implode(' ', $errors),
    		'PERSON' => $person,
    		'PHONE' => $phone,
    		'QUESTION' => $question
    		)
    	);
    
    	$template->send();
    }
    ?>


    Нам хочется привнести некоторые удобства для клиента. И на помощь нам приходит…

    JavaScript

    <script language="text/javascript">
    <!-- //
    function form_submit()
    {
    	if  (
    		( document.forms['callback'].person.value == '' ) ||
    		( document.forms['callback'].phone.value == '' ) ||
    		( document.forms['callback'].question.value == '' )
    	)
    	{
    		alert('Не заполнены обязательные поля');
    	}
    
    	return;
    }
    // -->
    </script>


    Функция alert использована лишь в качестве учебного примера, по статистике часть пользователей закроет его не читая, потому что выводится оно в дизайне ОС и не воспринимается как часть веб-страницы.

    Еще раз обращу внимание на то, что серверная проверка все равно необходима.

    Каждый раз, когда меня просят рассказать зачем это нужно, я вспоминаю примерно год 2004, когда я, начинающий веб-разработчик, познакомился по переписке со славным парнем с Украины, бывшим тогда автором собственной CMS. Пытаясь перенимать опыт, я как-то поинтересовался у него, как он производит обработку ошибок. Выяснилось, что делает он это только при помощи JavaScript. Пришлось уже самому делиться опытом о том, что можно сохранить страницу на компьютер, подменить адрес обработчика формы и отправить данные без валидации. И если валидации не будет уже на сервере, некорректные данные будут восприняты как нормальные, а дальше все будет зависеть от логики приложения. Позже в процессе работы я часто встречал подобные уязвимости. Вывод напрашивается сам собой — если веб-приложение пишется без понимания особенностей взаимодействия браузера клиента и сервера, результат может быть разным.

    Проходит время, сложность форм растет и я обращаю свой взор на следующую технологию.

    AJAX

    Теперь проверка для удобства вынесена в отдельный метод и немного дополнена, в результате получаем примерно такой код:

    <?php
    
    $errors = array();
    
    if ( empty($person) || empty($phone) || empty($question) )
    {
    	$errors[] = 'Не заполнены обязательные поля';
    }
    
    $ajax = isset($_REQUEST['ajax']);
    
    if ( count($errors) )
    {
    	if ( empty($ajax) )
    	{
    		$template->assign_switch('errors', 1);
    	
    		$template->vars(array(
    			'ERRORS' => implode(' ', $errors),
    			'PERSON' => $person,
    			'PHONE' => $phone,
    			'QUESTION' => $question
    			)
    		);
    
    		$template->send();
    	}
    	else
    	{
    		$ajax_handler->send(array(
    			'errors' => 1,
    			'error_text' => implode(' ', $errors),
    			)
    		);
    	}
    }
    else
    {
    	$ajax_handler->send(array(
    		'errors' => 0,
    		'error_text' => '',
    		)
    	);
    }
    ?>


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

    Вредный совет

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

    <?php
    
    if ( count($errors) )
    {
    	if ( empty($ajax) )
    	{
    		header('Location: form.php?error_code=1');
    		exit();
    	}
    }
    ?>


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

    Заключение

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