js http request дмитрий котеров
DESCRIPTION
TRANSCRIPT
JsHttpRequest: JsHttpRequest: кроссбраузерный кроссбраузерный AJAX,AJAX, фоноваяфоновая закачка файлов, "прозрачное" закачка файлов, "прозрачное" программирование программирование backend-backend-скриптовскриптов
Кроссбраузерность, закачка, Кроссбраузерность, закачка, отладкаотладка, , многомерные данные, многомерные данные, русский язык, ...русский язык, ...
Дмитрий Котеров,Дмитрий Котеров,архитектор и главный разработчик архитектор и главный разработчик MoiKrug.ruMoiKrug.ru компания Яндекскомпания Яндекс
[email protected]@koterov.ru
www.rit2007.ru
Что такое Что такое Web 2.0Web 2.0??
• © O'Reilly Media, 2004• Браузер есть
платформа• Данные – движущая
сила сайта• Демократия среди
пользователей, рейтинги, голосования
• Модель социальной сети
• Динамическая загрузка данных (AJAX)
www.rit2007.ru
AJAXAJAX: идеология или просто модное : идеология или просто модное слово?слово?
• Asynchronous JavaScript And XML
• Старое название – Remote Scripting
• Компоненты:– Frontend– Backend
www.rit2007.ru
АсинхронностьАсинхронность
1. Запустить запрос к серверу
2. Продолжить работу
3. Когда придет ответ, запустить обработчик
4. Обработать данные, изменить страницу
• Сложность AJAX-валидаторов• Событийно-ориентированное
программирование
www.rit2007.ru
Методы динамической загрузкиМетоды динамической загрузки
• XMLHttpRequest
• ActiveX
• <script>
• невидимый <iframe>
• <img> + cookies
• кто-нибудь знает другие?
www.rit2007.ru
XMLHttpRequestXMLHttpRequest
• Многословный интерфейс• Не работает в IE5, IE6, Opera7• Неудобство передачи
сложноструктурированных данных• Неудобство работы с XML (далее...)
var req = new XMLHttpRequest();req.onreadystatechange = function() { if (req.readyState == 4) { alert(req.responseText); }}req.open("POST", "xml_backend.php", true);req.send("a=123&b=456");
www.rit2007.ru
XMLHttpRequest: XMLHttpRequest: обработка обработка XMLXML
<?phpheader('Content-type: application/xml');echo '<?xml version="1.0" encoding="windows-1251"?>';echo '<root><time>' . date('r') . '</time></root>';?>
var xml = req.responseXML;var timeNode = xml.getElementsByTagName('time')[0];alert(timeNode.childNodes[0].nodeValue);
• Сложно формировать • Сложно разбирать• Неустойчивость к ошибкам в backend
www.rit2007.ru
XMLHttpRequest: XMLHttpRequest: АЯКСАЯКС или АЙАЙили АЙАЙ??
• JSON: генерацияjson = { "time": "10:00 PM", "date": { "day": 10, "month": "April", "year": 2007 }}
• PHP: json_encode()
• JSON: разборvar r;eval("r = " + json);
• AJAJ – Asynchronous JavaScript and JSON
• Все еще неустойчивость к ошибкам
www.rit2007.ru
XMLHttpRequest: XMLHttpRequest: русские кодировкирусские кодировки
• JavaScript – полностью Unicode-язык• escape(): кодирование в UCS2BE:
%u1234%u5678– работает даже в очень старых браузерах– сильно увеличивает объем
• encodeUriComponent(): кодирование в UTF-8 с urlencode: %B2%A3– работает начиная с IE 5.5+
• Однобайтовых кодировок нет
www.rit2007.ru
ActiveXActiveX
• Работает только в IE5, IE6
• Должны быть разрешены ActiveX
• В IE 5.0 иногда возникают проблемы с кодировками (в backend передаются "?")
www.rit2007.ru
ActiveX ActiveX и и XMLHttpRequest: XMLHttpRequest: кроссбраузерная инициализациякроссбраузерная инициализация
var req = null; if (window.XMLHttpRequest) { // Mozilla, Safari и т.д. req = new XMLHttpRequest(); } else if (window.ActiveXObject) { // Internet Explorer с включенными ActiveX. try { req = new ActiveXObject("Msxml2.XMLHTTP") } catch (e) { try { req = new ActiveXObject("Microsoft.XMLHTTP") } catch (e) {} } } if (!req) { alert("XMLHttpRequest не поддерживается в этом браузере!"); return; }
www.rit2007.ru
<script><script>
var script = document.createElement('script');script.src = 'script_backend.php?id=1&a=123';document.appendChild(script);
<?php
$id = $_GET['id'];
$a = $_GET['a'];
$json = json_encode(process($a));
?>
dataReady({
id: "<?=$id?>",
data: <?=$json?>
});
www.rit2007.ru
<script>: <script>: проблемыпроблемы
• Поддерживается только GET• Нельзя передать много данных• Нужно явно передавать ID• Opera: загрузка <script> - блокирующая
(синхронная) операция• Opera: только
div.innerHTML = 'text<script></script>'• В некоторых браузерах не работает
вообще (например, Opera 7.10)
www.rit2007.ru
<iframe><iframe>
var iframe = document.createElement('iframe');iframe.name = 'dyna';document.appendChild(iframe);form.target = iframe.name;form.submit();
<?php
$id = $_GET['id'];
$a = $_GET['a'];
$json = json_encode(process($a));
?>
<script>
dataReady({
id: "<?=$id?>",
data: <?=$json?>
});
</script>
www.rit2007.ru
<iframe>:<iframe>: плюсы и минусы плюсы и минусы
• Плюсы– нет проблем с русскими кодировками– отправка формы целиком– поддержка методов GET и POST– можно закачивать файлы
• element.cloneNode() не работает в Safari для файловых полей
• Минусы– щелчок– засорение history– баги в FF с кнопкой Назад– большой расход памяти– плохая кроссбраузерность
www.rit2007.ru
<img> + cookies<img> + cookies
var img = document.createElement('img');img.src = 'img_backend.php?id=1&a=123';img.onload = function () { alert(document.cookies);}document.appendChild(img);
<?php$data = process();setcookie('result', json_encode($data), '/');?>
• Исключительно высокая кроссбраузерность!• Но: должны быть включены cookies
www.rit2007.ru
Обзор всех методовОбзор всех методов: : проблемыпроблемы
• Совершенно разные интерфейсы
• Трудно передавать сложные данные:{ some: value, other: { key: value } }
• Трудно принимать сложные данные
• Разные методы для разных браузеров
• Русскоязычные кодировки
• Обработка ошибок в backend
www.rit2007.ru
Есть ли решение?Есть ли решение?
• ДА!
• JsHttpRequest решает все перечисленные проблемы
www.rit2007.ru
Упрощенный интерфейс вызоваУпрощенный интерфейс вызова
// Frontend: JavaScriptJsHttpRequest.query( address, // путь до backend data, // массив/объект JavaScript onreadyfunc(result, errors), // callback nocache // если true, кэш запрещен );
// Backend: PHP
require_once 'JsHttpRequest/JsHttpRequest.php";
$JsHttpRequest =& new JsHttpRequest("windows-1251");
...
// Получаем данные из $_GET, $_POST, $_FILES, $_REQUEST
...
global $_RESULT;
$_RESULT = array(данные для frontend-а)
www.rit2007.ru
ПримерПример: suggest (frontend): suggest (frontend) JsHttpRequest.query( // Адрес backend-сценария. 'suggest_backend.php', // Так передаются параметры backend-у { 'str': st, // первые буквы слова 'num': 10 // число элементов списка для вывода }, // Функция вызывается, когда backend подготовил данные. function(result, errors) { // Записать сообщения об ошибках в <div>. document.getElementById("debug").innerHTML = errors; // Сформировать результат. var list = document.getElementById("list"); list.length = 0; // удалить все строки списка for (var i = 0; i < result.list.length; i++) { list[i] = new Option(result.list[i]); } }, // Не запрещать кэширование одинаковых запросов. false );
www.rit2007.ru
ПримерПример: suggest (backend): suggest (backend)<?php// Загрузить библиотеку JsHttpRequest.require_once "JsHttpRequest/JsHttpRequest.php";// Инициализация. Обязательно указывайте кодировку сценария!$JsHttpRequest =& new JsHttpRequest("windows-1251");
// Получить запрос: первые буквы слова и число элементов.$str = $_REQUEST['str'];$num = $_REQUEST['num'];...
// Передать результат поиска во frontend.global $_RESULT;$_RESULT = array( "str" => $str, "list" => $found,);
// Выводим также отладочную информацию.echo sprintf("Выборка %d из %d слов.", count($found), count($words));?>
www.rit2007.ru
Закачка файлов "Закачка файлов "AJAX-AJAX-ом"ом"
• Проблема безопасности
• Библиотеке передается не файл, а элемент выбора файла
• Проблема в Safari: <input type="file"> нельзя клонировать и перемещать в другую форму
www.rit2007.ru
Закачка файлов "Закачка файлов "AJAX-AJAX-ом"ом"<form enctype="multipart/form-data"> <input type="file" name="e_file" id="e_file"></form>...JsHttpRequest.query( 'backend.php', { 'str': { nested: st }, 'upl': document.getElementById('e_file') }, function(result, errors) { ... });
<?php...$str = $_REQUEST['str']['nested'];
// Работаем с $_FILES, как обычно!// Элемент в $_FILES приходит с именем "upl", а не "e_file".$tmpName = $_FILES['upl'];?>
www.rit2007.ru
Отправка формы целикомОтправка формы целиком
<form enctype="multipart/form-data" id="e_form"> <input type="file" name="e_file"> <input type="text" name="str"></form>...JsHttpRequest.query( 'backend.php', document.getElementById('e_form') function(result, errors) { ... });
<?php...$str = $_REQUEST['str'];
// Работаем с $_FILES, как обычно!// Имена элементов а $_REQUEST и $_FILES совпадают с // именами полей формы.$tmpName = $_FILES['e_file'];?>
www.rit2007.ru
Русскоязычные кодировкиРусскоязычные кодировки
• Работа с кодировками полностью прозрачна как для frontend, так и для backend
• JsHttpRequest может работать с windows-1251 и koi8-r и при отсутствии iconv (собственные таблицы)
• Для ускорения в PHP 5.2 автоматически используется json_encode()
www.rit2007.ru
СессииСессии
• Работа с сессиями полностью прозрачна и происходит стандартными средствами PHP
www.rit2007.ru
Выбор метода и загрузчикаВыбор метода и загрузчика
JsHttpRequest.query( 'GET backend.php', ...);
JsHttpRequest.query( 'xml.POST backend.php', ...);
JsHttpRequest.query( 'http://other.host.ru/backend.php', ...);
• По умолчанию выбирается наилучший загрузчик и метод, совместимые с браузером
• Возможна загрузка с "чужого" домена (script, form)
www.rit2007.ru
КэшированиеКэширование
• Четвертый параметр JsHttpRequest.query().
• Если кэширование включено, то при попытке повторной загрузки по тому же самому запросу данные берутся из кэша.
• Не работает для закачки файлов, а также для отправки формы целиком.
www.rit2007.ru
Отладка и перехват ошибок Отладка и перехват ошибок в в backendbackend
<?phprequire_once "JsHttpRequest/JsHttpRequest.php";$JsHttpRequest =& new JsHttpRequest("windows-1251");...ini_set('display_errors', true);call_to_undefined_function();?>
JsHttpRequest.query( 'backend.php', { ... }, function(result, errors) { if (errors) alert(errors); });
• Кто сказал, что фатальные ошибкиPHP нельзя перехватить?
www.rit2007.ru
Отладка и перехват ошибок Отладка и перехват ошибок в в backendbackend
• Пример данных, сгенерированных backend-ом
JsHttpRequest.dataReady({ "id": "123", "js": { "str": "строка", ... }, "text": "Сообщения и ошибки."})
www.rit2007.ru
Ошибки во Ошибки во frontendfrontend
• Все ошибки генерируются в виде исключений JavaScript: throw new Error(...)
1. 'Invalid FORM element detected: name=%, tag=%',2. 'If used, <form> must be a single HTML element in the list.',3. 'JavaScript code generated by backend is invalid!\n%',4. 'Cannot use so long query with GET request (URL is larger than % bytes)',5. 'Unknown loader: %',6. 'No loaders registered at all, please check JsHttpRequest.LOADERS array',7. 'Cannot find a loader which may process the request. Notices are:\n%',8. 'Method setRequestHeader() cannot work together with the % loader.'9. 'Cannot use XMLHttpRequest or ActiveX loader: not supported',10. 'Cannot use XMLHttpRequest to load data from different domain %',11. 'Cannot use XMLHttpRequest loader or ActiveX loader, POST method: headers setting is not supported,
needed to work with encodings correctly',12. 'Cannot use XMLHttpRequest loader: direct form elements using and uploading are not implemented'13. 'Cannot use SCRIPT loader: it supports only GET method',14. 'Cannot use SCRIPT loader: direct form elements using and uploading are not implemented'15. 'Element "%" does not belong to any form!',16. 'Element "%" belongs to a different form. All elements must belong to the same form!',17. 'Attribute "enctype" of the form must be "%" (for IE), "%" given.'
www.rit2007.ru
Интерфейс, совместимый с Интерфейс, совместимый с XMLHttpRequestXMLHttpRequest
var req = new XMLHttpRequest();req.onreadystatechange = function() { if (req.readyState == 4) { alert(req.responseText); }}req.open("GET", 'backend.php', true);req.send(null);
var req = new JsHttpRequest();req.onreadystatechange = function() { if (req.readyState == 4) { alert(req.responseText); }}req.open("GET", 'backend.php', true);req.send(null);
Найдите 10 отличий…
• Элементарная интеграция с prototype js
• Легкая миграция существующих приложений
www.rit2007.ru
Модульная архитектураМодульная архитектура
• Код библиотеки состоит из частей:– Базовый модуль (неотключаемый)– Модуль поддержки XML– Модуль поддержки script– Модуль поддержки form
• Любой модуль можно удалить из кода даже "вручную"• mini/JsHttpRequest-script.js: только загрузчик script (8.2К)• mini/JsHttpRequest-script-xml.js: только script и xml• debug/*.js: полные версии исходников с комментариями• mini/*.js: "минимизированные" версии (dojomin)• "Минимизированная" версия со всеми загрузчиками: 14К
www.rit2007.ru
Обеспечение кроссбраузерностиОбеспечение кроссбраузерности
• Автоматизированный test framework
• На каждый багфикс – один тест
• На каждую сгенерированную ошибку – один тест
• Главная сложность при тестировании – принципиальная асинхронность
www.rit2007.ru
Backend-Backend-ы на неы на не-PHP-PHP
• За время существования библиотеки зарегистрированы случаи создания умельцами backend-модулей на:– perl– c– parser
• К сожалению, все они не дотягивают до production-версий
• Но никаких препятствий нет!
www.rit2007.ru
РезюмеРезюме
• Кроссбраузерность (IE5.0+ без ActiveX, Mozilla 1.7+, Firefox 1.0+, Opera 7.20+, Safari)
• Прозрачная работа с русскими кодировками• Закачка файлов методом AJAX• Поддержка отладочных возможностей PHP• Двусторонняя передача многомерных структур
PHP <-> JavaScript• Поддержка сессий PHP• Выбор оптимального загрузчика• Компонентность• Совместимый с XMLHttpRequest интерфейс
www.rit2007.ru
Приходите к нам работать!Приходите к нам работать!
• МойКруг.ру теперь – сервис Яндекса• Мы расширяем свою команду!• Открыты вакансии для:
– верстальщиков со знанием Smarty– отличных PHP-программистов
Ждем Ваши резюме на http://moikrug.ru/hire/