Получение входных данных через Web

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

Решение
Каждый API предлагает средство доступа к именам и значениям входных параметров в среде исполнения веб-сценария.

Обсуждение
В предыдущих разделах обсуждалось извлечение информации из MySQL и ее использование для формирования различных видов вывода: статического текста, гиперссылок и элементов формы. В этом разделе мы рассмотрим обратную задачу – получение входных данных через Web. Приемы, обсуждае-мые в этом разделе, можно использовать, например, для извлечения содержимого формы, отправленной пользователем. Интерпретируем полученные значения как ключевые слова поиска, затем выполняем запрос к каталогу товаров и показываем пользователю результат. В данном случае Web используется для получения информации, по которой мы определяем, что именно интересует заказчика. По этой информации строим запрос и выводим результаты. Если форма представляет собой опрос, подписку на список рассылки или голосование, то можно просто сохранить значения, используя полученные данные для создания новой записи в базе данных (или для обновления существующей записи).

Сценарий, получающий входные данные через Web и использующий их для взаимодействия с MySQL, обычно обрабатывает информацию поэтапно:

1.


Извлекает входные данные из окружения. При получении запроса, содержащего входные параметры, веб-сервер помещает их в окружение сценария, обрабатывающего запрос, а сценарий запрашивает параметры у окружения. Может потребоваться декодирование специальных символов в параметрах для выявления истинных значений, переданных клиентом, если ваш API не делает этого сам при извлечении. (Например, может потребоваться преобразовать %20 в пробел.)

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

3. Создает запрос на основе входных данных. Обычно входные параметры используются для добавления записи в базу данных или извлечения информации из базы для их отображения клиенту.


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

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

Входные данные могут быть получены через Web различными способами, два из которых встречаются чаще:• Как составляющая запроса GET, тогда входные параметры добавляются в конец URL. Например, следующий URL вызывает сценарий на PHP price_quote.php и указывает параметры item и quantity со значениями D-0214 и 60:

http://apache.snake.net/mcb/price_quote.php?item=D-0214&quantity=60

Такие запросы обычно получаются, когда пользователь выбирает гиперссылку или отправляет форму, у которой в теге
указано method="GET".

Список параметров в URL начинается с ? и состоит из пар имя=значение, разделенных символами ; или &.


(Можно поместить информацию и в середину URL, но здесь я не буду об этом рассказывать.)

• Как составляющая запроса POST при отправке формы, у которой в теге указано method="POST". Содержимое формы для запроса POST отправляется как входные параметры в теле запроса, а не в конце URL.

Вам могут встретиться и другие виды ввода, например, загружаемые файлы. Они отправляются при помощи запросов POST, но внутри специальной формы.

Когда вы собираете входные параметры для веб-сценария, может понадобиться информация о том, каким способом они были получены. (Некоторые API различают ввод, полученный при помощи GET и POST, некоторые – нет.) Однако после того, как информация извлечена, метод запроса уже не имеет значения. На этапах проверки корректности и построения запроса уже не важно, посредством какого метода были получены параметры.

Дистрибутив recipes содержит ряд сценариев обработки входных параметров в каталоге apache/params (tomcat/mcb для JSP). Каждый сценарий позволяет отправлять запросы GET и POST и показывает, как извлекать и выводить значения отправленных параметров. Изучите эти сценарии, чтобы понять, как методы извлечения параметров используются в разных API. Вызываемые сценариями функции хранятся в библиотечных модулях каталога lib дистрибутива.

Правила извлечения Web-ввода
Для получения входных параметров, переданных в сценарий, необходимо изучить возможности вашего API, чтобы понимать, что он может вам предложить, а какие операции вы должны проделать самостоятельно. Например, вы должны знать ответы на следующие вопросы:

• Как определить, какие параметры доступны?

• Как извлечь значение параметра из окружения?

• Полученные значения – это реальные значения, переданные клиентом, или их необходимо раскодировать?

• Как обрабатываются параметры с множественными значениями (например, если выбрано несколько элементов группы флажков)?

• Какой символ API предполагает увидеть в качестве разделителя параметров, передаваемых в URL? В одних API это может быть &, в других – ;.Последнее предпочтительнее, так как символ ;, в отличие от &, не является специальным для HTML, но многие броузеры и другие пользовательские агенты разделяют параметры при помощи &. Если вы создаете URL в сценарии, включающем параметры в конец адреса, используйте символ разделителя параметров, который будет понятен принимающему сценарию.

Perl
Модуль Perl CGI.pm обеспечивает доступ сценариев к входным параметрам при помощи функции param(). Эта функция упрощает жизнь создателю сценария при работе с параметрами, переданными любым из методов GET и POST.

Если форма, содержащая параметры id и name, была передана при помощи POST, вы можете обрабатывать ее так же, как если бы параметры были указаны в конце URL и переданы в GET. Кроме того, не придется заниматься декодированием, функция param() все сделает сама.

Для получения списка имен всех доступных параметров вызовите param() без аргументов:

@names = param ();

Чтобы получить значение определенного параметра, передайте его имя в param():

$id = param ("id");
@options = param ("options");

В скалярном контексте param() возвращает значение параметра, если оно одно, первое значение, если у параметра их несколько, и undef – если параметр недоступен. В контексте массива param() возвращает список, содержащий все значения параметра, или пустой список, если параметр недоступен.

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

Имейте в виду, что значение параметра может быть указано как пустое. Для полноты можно проверять обе возможности. Например, чтобы проверить параметр age и присвоить ему значение по умолчанию unknown, если параметр отсутствует или пуст, сделайте следующее:

$age = param ("age");
$age = "unknown" if !defined ($age) || $age eq "";
CGI.pm воспринимает и ;, и & как символы-разделители параметров в URL.

PHP
В PHP к входным параметрам можно обращаться разными способами в зависимости от версии PHP и параметров конфигурации:

• Если установлен параметр register_globals, то входные параметры присваиваются глобальным переменным с такими же именами. Тогда к значению поля id можно обращаться как к переменной $id независимо от того, отправлен запрос через GET или POST.• Если установлен параметр track_vars, то входные параметры доступны в массивах $HTTP_GET_VARS и $HTTP_POST_VARS. Например, если в форме есть поле id, можно обратиться к его значению при помощи $HTTP_GET_VARS["id"] или $HTTP_POST_VARS["id"] в зависимости от того, каким методом была отправлена форма. $HTTP_GET_VARS и $HTTP_POST_VARS необходимо объявить, используя ключевое слово global, чтобы они были доступны в неглобальных сценариях, например, внутри функции.

• Начиная с версии PHP 4.1 входные параметры доступны в массивах $_GET и $_POST. Они аналогичны $HTTP_GET_VARS и $HTTP_POST_VARS, только являются «суперглобальными» в том смысле, что они автоматически доступны в любом контексте. (Например, нет необходимости в объявлении $_GET и $_POST при помощи global внутри функций.) В настоящее время массивы $_GET и $_POST являются предпочтительным средством получения входных параметров.

Параметры track_vars и register_globals могут быть указаны при сборке или заданы в файле php.ini. Начиная с версии PHP 4.0.3 параметр track_vars всегда установлен, и я подозреваю, что и в ранних версиях он чаще всего был включен. Поэтому будем считать, что в вашей версии PHP track_vars установлен.

Параметр register_globals обеспечивает удобный доступ к входным параметрам через глобальные переменные, но PHP-разработчики рекомендуют не использовать его из соображений безопасности. Почему? Предположим, что вы пишете сценарий, который запрашивает у пользователя пароль, представленный переменной $password. Проверить пароль в сценарии можно так:

if (check_password ($password))
$password_is_ok = 1;

Если пароль подходит, то сценарий устанавливает $password_is_ok в 1. В противном случае $password_is_ok остается неустановленным (аналог логического значения «ложь»). Но предположим, что установлен параметр register_variables, и кто-то вызывает сценарий так:

http://your.host.com/chkpass.php?password_is_ok=1

В этом случае PHP видит, что параметр password_is_ok установлен в 1, и устанавливает переменную $password_is_ok в 1. В результате при исполнении сценария $password_is_ok равна 1 независимо от того, какой пароль был введен или даже пароль вовсе не был введен! Проблема register_globals в том, что сторонние пользователи могут задавать значения по умолчанию для глобальных переменных ваших сценариев. Одним из решений может стать отключение register_globals, тогда за значениями входных параметров придется обращаться к глобальным массивам ($_GET, $_POST). Если вы не хотите этого делать, не рассчитывайте на то, что переменные PHP не имеют начальных значений. Лучше явно задать для них какие-то исходные значения, если конечно вы не хотите, чтобы глобальная переменная устанавливалась в значение входного параметра. Чтобы гарантировать присвоение значения переменной $password_is_ok вне зависимости от результата проверки, код проверки пароля следует переписать так:

$password_is_ok = 0;
if (check_password ($password))
$password_is_ok = 1;

Сценарии на PHP в этой книге не полагаются на register_globals и получают входные параметры из глобальных массивов.

Извлечение параметров в PHP осложняется возможной необходимостью декодирования в зависимости от значения параметра конфигурации magic_quotes_gpc. Если магические кавычки разрешены, то все символы кавычек, обратного слэша и NUL в значениях входных параметров экранируются обратным слэшем. Так вы можете «проскочить» один этап, извлекая значения и используя их непосредственно в строке запроса. Однако такая возможность удобна только в том случае, если вы собираетесь использовать веб-ввод в запросе без предварительной обработки и проверки корректности, а это весьма опасно. Необходимо сначала проверить ввод, так или иначе избавившись ото всех символов обратного слэша. То есть разрешать магические кавычки особого смысла нет.

Извлечение ввода в сценариях на PHP становится интересной задачей: входные параметры могут поступать из различных источников и могут содержать или не содержать дополнительные символы обратного слэша. Если вы можете управлять сервером и устанавливать значения параметров конфигурации, то, конечно же, можете создавать сценарии на основе этих параметров. Но если вы не управляете сервером или пишете сценарии, которые будут работать на нескольких компьютерах, то заранее неизвестно, какой будет конфигурация. К счастью, немного постаравшись, вы сможете написать достаточно универсальный код извлечения параметров, корректно работающий при весьма небольших допущениях относительно вашего PHP-окружения. Рассмотрим функцию get_param_val(), которая принимает в качестве аргумента имя параметра и возвращает соответствующее значение. Если параметр недоступен, функция возвращает неустановленное (unset) значение:

function get_param_val ($name)
{
global $HTTP_GET_VARS, $HTTP_POST_VARS;
unset ($val);
if (isset ($_GET[$name]))
$val = $_GET[$name];
else if (isset ($_POST[$name]))
$val = $_POST[$name];
else if (isset ($HTTP_GET_VARS[$name]))
$val = $HTTP_GET_VARS[$name];
else if (isset ($HTTP_POST_VARS[$name]))
$val = $HTTP_POST_VARS[$name];
if (isset ($val) && get_magic_quotes_gpc ())
$val = strip_slash_helper ($val);
return (@$val);
}

Если вы хотите применить функцию для получения единственного значения параметра id, вызовите ее так:

$id = get_param_val ("id");

Можно проверить $id, чтобы определить, присутствует ли параметр id во вводе:

if (isset ($id))
... id parameter is present ...
else
... id parameter is not present ...

Если поле формы может иметь несколько значений (как группа флажков или прокручиваемый список с множественным выбором), необходимо представить его в форме именем, которое заканчивалось бы на []. Например, списковый элемент, созданный на основе столбца accessories типа SET из таблицы cow_order, имеет одно значение для каждого разрешенного значения множества. Для того чтобы PHP воспринимал значения списка как массив, назовите поле не accessories, а accessories[].

При отправке формы PHP помещает массив значений в параметр с таким же именем, но без [], обратиться к которому можно так:

$accessories = get_param_val ("accessories");

Переменная $accessories будет массивом. (Вне зависимости от того, имеет ли параметр множество значений, одно или ни одного. Определяющим фактором является не то количество значений, которое параметр имеет в действительности, а то, использовалась ли нотация [] для соответствующего поля формы.)

Функция get_param_val() проверяет в поиске значений параметров массивы $_GET, $_POST, $HTTP_GET_VARS и $HTTP_POST_VARS. То есть она корректно работает с PHP 3 и PHP 4 для запросов, сделанных GET и POST, вне зависимости от того, включен ли параметр конфигурации register_globals. Функция предполагает лишь одно – параметр track_vars включен.

Функция get_param_val() работает корректно и вне зависимости от того, разрешены ли магические кавычки. Она использует вспомогательную функцию strip_slash_helper(), которая при необходимости удаляет символ обратного слэша из значений параметров:

function strip_slash_helper ($val)
{
if (!is_array ($val))
$val = stripslashes ($val);
else
{
reset ($val);
while (list ($k, $v) = each ($val))
$val[$k] = strip_slash_helper ($v);
}
return ($val);
}

Функция strip_slash_helper() определяет, скаляром или массивом является значение, и обрабатывает его соответствующим образом. Для значений-массивов используется рекурсия, так как в PHP 4 можно создавать вложенные массивы входных параметров.
Для того чтобы упростить получение списка имен всех параметров, напишем еще одну функцию:

function get_param_names ()
{
global $HTTP_GET_VARS, $HTTP_POST_VARS;
# Создать ассоциативный массив, в котором для каждого элемента
# имя параметра является и ключом, и значением.
# (Использование ассоциативного массива устраняет дубликаты.)
$keys = array ();
if (isset ($_GET))
{
reset ($_GET);
while (list ($k, $v) = each ($_GET))
$keys[$k] = $k;
}
else if (isset ($HTTP_GET_VARS))
{
reset ($HTTP_GET_VARS);
while (list ($k, $v) = each ($HTTP_GET_VARS))
$keys[$k] = $k;
}
if (isset ($_POST))
{
reset ($_POST);
while (list ($k, $v) = each ($_POST))
$keys[$k] = $k;
}
else if (isset ($HTTP_POST_VARS))
{
reset ($HTTP_POST_VARS);
while (list ($k, $v) = each ($HTTP_POST_VARS))
$keys[$k] = $k;
}
return ($keys);
}

Функция get_param_names() возвращает список имен параметров, присутствующих в массивах переменных HTTP, при этом если массивы частично совпадают, повторяющиеся имена удаляются. Возвращенное значение – это ассоциативный массив, в котором и ключи, и значения установлены в имена параметров, так что и ключи, и значения можно использовать как список имен. Следующий пример выводит имена, используя значения:

$param_names = get_param_names ();
while (list ($k, $v) = each ($param_names))
print (htmlspecialchars ($v) . "
\n");

Для сценариев на PHP 3 необходимо разделять параметры в URL символами &. По умолчанию этого требует и PHP 4, хотя в данном случае вы можете заменить символ-разделитель, используя конфигурационный параметр arg_separator в файле инициализации PHP.

Python
Модуль cgi для Python предоставляет доступ к входным параметрам, присутствующим в окружении сценария. Импортируйте этот модуль, затем создайте объект FieldStorage, используя одноименный метод:

import cgi
param = cgi.FieldStorage ()

Метод FieldStorage возвращает информацию о параметрах, переданных посредством GET или POST, поэтому вам не нужно знать, какой именно метод был использован для отправки запроса. Объект FieldStorage содержит по элементу для каждого параметра, присутствующего в окружении. Список доступных параметров можно получить так:

names = param.keys ()

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

val = param[name].value

Если у параметра несколько значений, то param[name] будет списком объектов MiniFieldStorage, имеющих атрибуты name и value. Все они имеют одинаковые имена (совпадающие с name) и одно из значений параметра. Для создания списка, включающего все значения такого параметра, выполните следующее:

val = []
for item in param[name]:
val.append (item.value)

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

param = cgi.FieldStorage ()
param_names = param.keys ()
param_names.sort ()
print "

Parameter names:", param_names, "

"
items = []
for name in param_names:
if type (param[name]) is not types.ListType: # это скалярptype = "scalar"
val = param[name].value
else: # это список
ptype = "list"
val = []
for item in param[name]: # обойти MiniFieldStorage
val.append (item.value) # элементы для получения значений
val = string.join (val, ",") # преобразовать в строку для вывода
items.append ("type=" + ptype + ", name=" + name + ", value=" + val)
print make_unordered_list (items)

Python порождает исключение, если вы пытаетесь обратиться к параметру, не включенному в объект FieldStorage. Чтобы избежать этого, используйте has_key() для проверки существования параметра:

if param.has_key (name):
print "parameter " + name + " exists"
else:
print "parameter " + name + " does not exist"

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

Модуль cgi ожидает, что параметры URL будут разделены символами &. Если вы формируете гиперссылку на сценарий, используя модуль cgi, и URL содержит параметры, не разделяйте их символами ;.

Java
Для страниц JSP доступ к параметрам запроса обеспечивает неявный объект request при помощи следующих методов:

getParameterNames()
Возвращает перечень объектов String, по одному для каждого имени параметра запроса.

getParameterValues(String name)
Возвращает массив объектов String, по одному для каждого значения, сопоставленного параметру, или null, если параметр не существует.

getParameterValue(String name)
Возвращает первое значение параметра или null, если параметр не существует.

Следующий пример показывает, как использовать эти методы для вывода параметров запроса:

<%@ page import="java.util.*" %>

    <%Enumeration e = request.getParameterNames ();
    while (e.hasMoreElements ())
    {
    String name = (String) e.nextElement ();
    // использовать массив, если параметр имеет несколько значений
    String[] val = request.getParameterValues (name);
    out.println ("
  • name: " + name + "; values:");
    for (int i = 0; i < val.length; i++)
    out.println (val[i]);
    out.println ("
  • ");
    }
    %>


Параметры запроса также доступны внутри тегов JSTL посредством специальных переменных param и paramValues. Переменная param[name] возвращает первое значение указанного параметра и поэтому лучше всего подходит для параметров с единственным значением:

color value:


Переменная paramValues[name] возвращает массив значений параметра, поэтому ее удобно использовать для параметров, которые могут иметь несколько значений:

accessory values:



Вы также можете обратиться к параметру, используя точечное обозначение, если имя параметра разрешено как имя свойства объекта:

color value:

accessory values:



Для вывода списка объектов параметров с атрибутами key и value обработаем в цикле переменную paramValues:



  • name:
    ;
    values:





Для создания URL, указывающего на страницы JSP и содержащего в конце параметры, следует разделять такие параметры символами &.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела MySQL на эту тему:
Ведение журнала Apache с помощью MySQL
Выполнение поиска и получение результатов
Журнал доступа к веб-странице
Загрузка в форму записи базы данных
Использование ввода через Web для формирования запросов