Показать сообщение отдельно

Способы решения проблем с кодировкой в Ajax
  #2  
Старый 04.12.2007, 00:39
LeverOne
Познающий
Регистрация: 22.02.2006
Сообщений: 67
Провел на форуме:
4155100

Репутация: 2033
Exclamation Способы решения проблем с кодировкой в Ajax

[= Суть проблемы =]

Ответ сервера при использовании Ajax по умолчанию приходит в кодировке "utf-8".
Если запрашиваемая страница изначально имеет кодировку "windows-1251", то кириллические символы будут безвозвратно искажены свойством responseText. В таком случае вывод ответа сервера будет представлен знаками вопроса "?", или другими неопознанными символами.

[= Способ решения =]
Их много. Все способы можно разделить на две группы: изменение кодировки на стороне сервера и на стороне клиента.

Когда мы имеем доступ к серверу, особых проблем обычно не возникает: хедеры, встроенные функции, перекодировка по таблицам символов. Описаний этого полно в нете.

Обратная ситуация. Например, нашли мы уязвимость XSS, желаем использовать Ajax для незаметного получения каких-либо данных с других страниц на сервере. При этом: во-первых, кодировка у страниц "windows-1251", во-вторых, сервер в ответе на Ajax-запрос не указывает кодировку пересылаемых данных.

В таких случаях возникает необходимость изменить кодировку уже на стороне клиента
.

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

[= Вариант, работающий во всех основных браузерах =]

test.html (необходимо убирать пробелы-разрывы, которые вставляет сайт)

PHP код:
<html>
<
div id=qwerty></div>
<
script>

if (
window.execScript)
   
window.execScript('Function BinaryToString(Binary) \r cl1=1 \r cl2=1 \r cl3=1 \r L=LenB(Binary) \r Do While cl1<=L pl3=pl3&Chr(AscB(MidB(Binary,cl1,1))) \r cl1=cl1+1 \r cl3=cl3+1 \r If cl3>300 Then \r pl2=pl2&pl3 \r pl3="" \r cl3=1 \r cl2=cl2+1 \r If cl2>200 Then \r pl1=pl1&pl2 \r pl2="" \r cl2=1 \r End If \r End If \r Loop \r BinaryToString=pl1&pl2&pl3 \r End Function''VBScript');

var 
request=false;
try {
request=new XMLHttpRequest();}
catch (
forIE) {request=new ActiveXObject('Msxml2.XMLHTTP');}

function 
ReadFile() 
{
 
request.open("GET"'text.html');
  if (
request.overrideMimeType)
     { 
       
request.overrideMimeType('text/html; charset=windows-1251');
       
request.onreadystatechange=override;
     }
 else
       
request.onreadystatechange=readResponse;
 
request.send(null);
}

function 
override()
{
 if (
request.readyState == 4
   {
    
request.open("GET"'text.html');
    
request.onreadystatechange=readResponse;
    
request.send(null);
   }
}

function 
readResponse()

 if (
request.readyState == 4)
    { 
     if (
request.overrideMimeType
        
document.getElementById('qwerty').innerHTML=request.responseText;
     else
        
document.getElementById('qwerty').innerHTML=BinaryToString(request.responseBody);
    }
}
</
script>
<
input type=button value='Жмах' onclick='ReadFile()'>
</
html
text.html
Код:
<html>Здесь что-нибудь на русском</html>
А теперь по каждому браузеру в отдельности, чтобы стало понятно тем, кто не разобрался.


1) Internet Explorer


Объекты Msxml2.XMLHTTP и XMLHttpRequest (IE 7.0) в этом браузере имеют фирменное свойство - "responseBody". В нем находится ответ сервера не в обычном строчном, а в бинарном виде. То есть в том виде, когда еще он не распарсился в соответствии с какой-либо кодировкой. В данном случае именно эта сторона для наc имеет значение. Будем парсить бинарный ответ самостоятельно.

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

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

VBScript-функция

Код:
 <script language=vbscript>
Function BinaryToString(Binary)
  'Antonin Foller, http://www.pstruh.cz
  'Optimized version of a simple BinaryToString algorithm.
 
  Dim cl1, cl2, cl3, pl1, pl2, pl3, L
  cl1 = 1
  cl2 = 1
  cl3 = 1
  L = LenB(Binary)
  Do While cl1<=L
    pl3 = pl3 & Chr(AscB(MidB(Binary,cl1,1)))  
'Аналогов функций AscB() и MidB() в js нет.
    cl1 = cl1 + 1
    cl3 = cl3 + 1
    If cl3 > 300 Then
      pl2 = pl2 & pl3
      pl3 = ""
      cl3 = 1
      cl2 = cl2 + 1
      If cl2 > 200 Then
        pl1 = pl1 & pl2
        pl2 = ""
        cl2 = 1
      End If
    End If
  Loop
  BinaryToString = pl1 & pl2 & pl3
End Function
 
</script>
Внедряем vbs-функцию в js с помощью фирменной функции IE - execScript()

(убрать пробел в предпоследнем упоминании функции BinaryToString)

PHP код:
<html>
<
div id=qwerty></div>

<
script>

window.execScript('Function BinaryToString(Binary) \r cl1=1 \r cl2=1 \r cl3=1 \r L=LenB(Binary) \r Do While cl1<=L pl3=pl3&Chr(AscB(MidB(Binary,cl1,1))) \r cl1=cl1+1 \r cl3=cl3+1 \r If cl3>300 Then \r pl2=pl2&pl3 \r pl3="" \r cl3=1 \r cl2=cl2+1 \r If cl2>200 Then \r pl1=pl1&pl2 \r pl2="" \r cl2=1 \r End If \r End If \r Loop \r BinaryToString=pl1&pl2&pl3 \r End Function''VBScript');

var 
request = new ActiveXObject('Msxml2.XMLHTTP');

function 
ReadFile() 
{
 
request.open('GET''text.html');
 
request.onreadystatechange=readResponse;
 
request.send(null);
}

function 
readResponse()

 if (
request.readyState == 4document.getElementById('qwerty').innerHTML=BinaryToString(request.responseBody); // передаем в функцию BinaryToString бинарный ответ сервера в качестве параметра и помещаем в див результат ее работы
}
</
script>
<
input type=button value='Жмах' onclick='ReadFile()'>
</
html
2) FireFox, Netscape

Используется метод overrideMimeType() объекта реквест

PHP код:
<html>
<
div id=qwerty></div>
<
script>

var 
request = new XMLHttpRequest();
function 
ReadFile() 
{
 
request.open("GET"'text.html');
 
request.overrideMimeType('text/html; charset=windows-1251');    // метод переопределяет заголовок "Content-Type" от сервера
 
request.onreadystatechange=readResponse;
 
request.send(null);
}

function 
readResponse() 

 if (
request.readyState == 4)
   
document.getElementById('qwerty').innerHTML=request.responseText; }
</
script>

<
input type=button value='Жмах' onclick='ReadFile()'>
</
html
3) Opera

Метод overrideMimeType() предусмотрен в этом браузере в объекте реквест, однако в текущей версии 9.24 кодировку он не переопределяет.

У данного браузера имеется другая особенность. Состоит она в автоматическом определении кодировки контента. То есть, когда запрашиваешь аяксом страницу в 'windows-1251' первый раз (с хоста), она выводится искаженной. Во второй раз та же страница отображается уже корректно. Данную особенность мы и будем использовать.

PHP код:
<html>
<
div id=qwerty></div>
<
script>

var 
request = new XMLHttpRequest();

function 
ReadFile() 
{
 
request.open("GET"'text.html');             // первый запрос
 
request.onreadystatechange=override;
 
request.send(null);
}

function 
override()
{
 if (
request.readyState == 4
   {
    
request.open("GET"'text.html');     // второй запрос
    
request.onreadystatechange=readResponse;
    
request.send(null);
   }
}

function 
readResponse()

 if (
request.readyState == 4)    // во второй раз, поскольку мы не разорвали соединение, состояние готовности сразу равно 4, в свойство попадает ответ сервера, находящийся в памяти.
 
document.getElementById('qwerty').innerHTML=request.responseText
}
</
script>
<
input type=button value='Жмах' onclick='ReadFile()'>
</
html
В действительности не происходит ожидания ответа сервера при втором запросе, результат выводится сразу. Потеря времени ничтожна мала.

Автоматическое определение кодировки Оперой производится независимо от установленной пользователем (пользовательской кодировкой может не быть "win-1251"), а также от настроек кэширования.

Ключи поиска: кодировка Ajax кодировка перекодировка Ajax перекодировка UTF-8 Win-1251

Последний раз редактировалось LeverOne; 26.12.2007 в 21:51..