Ajax и кириллические символы

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

  1. AJAX - это технология асинхронного (как вариант) приема передачи данных по HTTP протоколу.
  2. Основным объектным классом на стороне клиента, реализующим собственно функционал, является XMLHttpRequest.
  3. Объект данного класса поддерживает прием передачу данных только методами GET и POST HTTP протокола.
Тепрь разберемся подробнее: Запрос к серверу методом GET. Как известно используя этот метод, все данные для сервера передаются в URL запроса и через заголовки. Пример:
GET Host http://localhost/site1/set_busy.php?status=Free&servID=12&duration=0 localhost
User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16
Accept */*
Accept-Language ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
Connection keep-alive
X-Requested-With XMLHttpRequest
Referer http://localhost/site1/index.php?userID=6
Cookie PHPSESSID=48a6861e659a87904d3a1d07958d8227
На сервере, в set_busy.php можно будет использовать конструкцию $_GET['status'] и аналогичные, чтобы получить значения переменных переданных в запросе. Но в URL нельзя передавать кириллические символы. Для этого используются escape-последовательности. Escape последовательность это: знак '%' за ним код символа в выбранной кодировке. В принципе вы можете таким образом передавать символы в любой кодировке, единственным условием является то, что необходимо использовать escape последовательности. Важно также отметить 2 факта:
  1. Используя метод GET мы НЕ можем в явном виде указать кодировку с помощью заголовка вида Content-type text/html charset="windows-1251" , просто потому что ничего кроме параметров запроса (т.е. все переменные передаются напрямую в строке запроса) мы серверу не отправляем (никакого контента просто нет).
  2. Если говорить об AJAX то объект класса XMLHttpRequest, который как говорилось раньше и осуществляет прием / передачу данных, делает это только в кодировке UTF-8, т.е. если мы даже дадим ему передать наши данные в windows-1251, то он все равно перекодирует их в utf-8, однако я уже забегаю вперед, потому что это относится уже к POST запросам, где мы передаем данные и можем указать в заголовке их кодировку.
Для перекодировки данных, вы можете написать собственные функции перевода в нужную вам кодировку, но намного удобнее и проще использовать уже имеющиеся JavaScript функции:
  1. escape()
  2. encodeURI()
  3. encodeURIComponent()
все три функции оставляют латинские и некоторые другие символы без изменений, все остальные символы заменяют escape-последовательностями. функция escape() - переводит кириллические символы в Unicode, при этом получаются символы вида %xx, либо %uxxxx, где xxxx - код символа именно в Unicode НЕ в UTF-8. Поскольку реализация этой функции слишком зависит от броузера, использовать ее не рекомендуется. функция encodeURI() - переводит кириллические символы в UTF-8, латинские символы и символы , / ? : @ & = + $ # остаются без изменений. функция encodeURIComponent() - переводит кириллические символы в UTF-8, латинские символы и символы !*()' остаются без изменений. Именно эта функция, кстати, одобрена W3C и используется в библиотеках jQuery и prototype.js для подготовки AJAX запросов. Итог: Чтобы передать серверу данные (с кириллическими символами) методом GET нам необходимо предварительно перекодировать эти данные вышеуказанными функциями (лучше в UTF-8). Эту процедуру скрыто от нас и делают библиотеки jQuery и prototype.js при подготовке GET запросов. На серверной стороне получив эти данные нам, как правило, необходимо будет перекодировать их в удобоваримую для нас windows-1251, для этого можно использовать, функции PHP:
  1. функция iconv: $status = iconv('UTF-8', 'windows-1251', $_GET['status']);
  2. либо, если установлен модуль mb_string, то функцию mb_convert_encoding, получаем приблизительно следующее: $status = mb_convert_encoding($_GET['status'], 'CP-1251', 'UTF-8');
Кстати, в PHP механизм работает так, потому что при получении запроса и формировании массива $_GET интерпретатор уже понимает escape - последовательности, и мы работаем в массивах со строками в определенной кодировке без всяких проблем. Запрос к серверу методом POST. Для начала, что такое POST? Это метод в котором сначала передаются заголовки в том числе и определяющие тип содержимого (контента), а затем передается сам контент в виде потока байт. Заголовок определяющий тип контента: Content-type. С помощью этого заголовка можно передать не только сам тип данных, но и например, их кодировку. Так, если мы говорим об обработке запроса на стороне сервера на PHP, то в зависимости от Content-type PHP заполняет либо массив $_POST и / или переменную $HTTP_RAW_POST_DATA. Массив $_POST PHP заполняет в том случае, когда в Content-type указано multipart/form-data или x-www-form-urlencoded. Почему же в этих случаях заполняется массив и каким образом, если мы говорили, что фактически контент это поток байт. Все просто, на самом деле указав эти виды Content-type, мы в качестве контента должны будет передать данные в виде пар: <параметр>=<значение>, объединенных знаком &. Т.е. практически как в GET, но с указанием кодировки и без ограничения на длину строки запроса в 255 символов, которая существует в GET. Пример контента для передачи методом POST, с указанными выше Content-type: p=qwerty&g=asdf, получив запрос, с таким контентом PHP сформирует массив $_POST, состоящий из двух элементов с ключами p, q и соответствующими им значениями. А если нужно передать не набор параметров, а просто текстовый или бинарный контент, в этом случае нужно использовать Content-type text/xml или application/octet-stream, и там же устанавливаем charset="windows-1251", в этом случае переданные данные попадают на стороне сервера с PHP в переменную $HTTP_RAW_POST_DATA. Что же происходит, когда мы отправляем наши данные в windows-1251 с помощью AJAX - POST запроса? А происходит, то о чем я говорил выше объект класса XMLHttpRequest переводит контент в кодировку UTF-8 и только потом отправляет на сервер. После чего на стороне сервера нужно не забыть перевести эти данные в нужную нам кодировку (как правило windows-1251), упомянутыми выше функциями. И последний вопрос, а как в этом случае поступают библиотеки jQuery и prototype.js? А поступают они точно также как и раньше они для формирования контента используют перекодировку функцией encodeURIComponent(). Ответ сервера на AJAX запрос. Для того чтобы ответ сервера на ваш AJAX запрос был передан броузеру (клиенту) в правильной кодировке, нужно перед отправкой данных послать заголовок Content-type с указанием типа данных и их кодировки. Например, функцией PHP: header('Content-type: text/html; charset=windows-1251'); Общие выводы.
  1. Напрямую через объект класса XMLHttpRequest передаются символы только в UTF-8.
  2. Можно передавать строки в любых других кодировках, если нелатинские символы при этом заменены escape последовательностями.
  3. В JavaScript существует 3 функции, которые делают escape замену нелатинских символов: escape(), encodeURI() и encodeURIComponent().
  4. На стороне сервера полученные по AJAX данные как правило нужно перекодировать функциями iconv или mb_convert_encoding
  5. Перед отправкой ответа клиенту, необходимо послать заголовок Content-type с указанием кодировки, в которой выдается ответ.
  6. Общая, обычная схема перекодировок в процессе работы с технологией AJAX следующая: клиент (броузер) делает запрос серверу и передает данные в UTF-8, сервер получает данные перекодирует в windows-1251, обрабатывает и выдает ответ клиенту в windows-1251. Это безусловно так, если ваши страницы написаны в windows-1251.