SRC: O’Rеillу. Prоgrаmming Flеx™ 2. Глава 16. Remote Data Communication.
Начало.
Обмен данными (Remote data communication – RDC) происходит на стадии выполнения приложения. Это затрагивает не одну только сторону клиента, а требует сетевого соединения для отправки и получения данных между клиентом и сервером. Flex-приложения поддерживают различные технологии обмена данными, основанные на соответствующих стандартах. Вот три основных категории способов реализации обмена данными во Flex-приложениях:
HTTP-соединение в режиме запрос/ответ
Эта категория включает в себя несколько соприкасающихся технологий. Задействуя компонент HTTPService из фрейморка Flex или класс API плеера URLLoader, вы можете отправлять и загружать несжатые данные, такие как блоки текста, кодированные URL-строки (далее — URL-encoded) и XML-пакеты. Также можно отправлять и получать SOAP-пакеты, используя компонент WebService фреймворка Flex. И наконец, использовать технологию под названием Flash Remoting, позволяющую отправлять и получать пакеты AMF, которые используют бинарный протокол, похожий на SOAP (но значительно меньший). Каждая технология преследует схожие цели – отсылать запросы и получать ответы по протоколу HTTP или HTTPS.
Обмен данными в реальном времени
Эта категория заключается в реализации непрерывного сокет-подключения. Flash Player поддерживает два типа сокет-подключений: те, которые требуют особого формата для пакетов (XMLSocket), и те, которые допускают неформатированную передачу (Socket). В обоих случаях, сокет-соединение между клиентом и сервером поддерживается постоянно, позволяя серверу «заталкивать» (push) данные к клиенту. Такое невозможно при использовании стандартных технологий HTTP в режиме запрос/ответ.
Загрузка/скачивание файлов
Эта категория основана на API-шном FileReference, реализованном во Flash-плеере, и позволяет напрямую загружать и скачивать файлы прямо в рамках Flex-приложения
Из этих трех основных технологий, наиболее четко отличается от двух остальных загрузка/скачивание файлов. Ясно, что загрузка/скачивание файлов применяется только в тех случаях, когда нужно организовать обмен файлами в приложении. А вот различие между HTTP-запросами/ответами и обменом в реальном времени не всегда столь очевидно.
Режим HTTP-запросов/ответов гораздо чаще употребим, нежели обмен данными в реальном времени. Хотя режим обмена данными в реальном времени необходим для некоторых приложений с активным фоновым обменом, он добавляет требования к пропускной способности сети в приложениях, потому что требует постоянного соединения с каждым пользователем. В отличие от такого подхода, соединение в режиме HTTP-запросов/ответом всегда инициируется клиентом в форме запроса. Тогда сервер возвращает ответ клиенту, затем соединение закрывается до тех пор, пока клиент вновь не отправит запрос. В большинстве случаев модель запрос-ответ более эффективна.
В этой главе мы сфокусируем внимание на двух видах удаленного обмена данными: запрос/ответ и загрузка/скачивание файлов. Приоритетно мы рассмотрим технику асинхронного обмена (запрос/ответ), потому что она чаще других применяется во Flex-приложениях. Мы также обсудим основы загрузки и скачивания файлов.
Понимание стратегий клиент-серверного обмена данными
Когда вы занимаетесь созданием Flex-приложения, которое задействует обмен данными, важно понимать стратегии, доступные для управления этими коммуникациями, и то, как правильно выбрать подходящую стратегию для приложения. Если вы только начали работать с Flex-платформой, важно потратить некоторое время на изучение того, как работает обмен данными в рамках Flash-плеера, и насколько это похоже или, наоборот, не похоже на то, с чем вы уже сталкивались, разрабатывая софт для других платформ. Например, некоторое из того, что вы освоили, работая с HTML-приложениями или приложениями Ajax, может оказаться полезным, но не следует считать, что Flex-приложения работают таким же способом, как приложения на других платформах.
Как вы уже хорошо знаете, все Flex-приложения выполняются Flash-плеером. За исключением некоторых Flex-приложений, созданных с использованием Flex Data Services, оставшееся большинство представляет собой компилированные swf-файлы, которые загружаются во Flash-плеер на стороне клиента. Swf-файлы сначала запрашиваются с сервера, но исполняются у клиента. Это означает, что динамические данные (любые данные, которые не прикомпилированы в swf) должны быть запрошены клиентом с сервера на стадии выполнения.
Поскольку Flex-приложения неизменны и самодостаточны, им не требуется запрашивать новые страницы и полностью обновлять экран, когда нужно отправить запрос и получить ответ. Такое поведение в некоторых Flex-приложениях имеет много общего с Ajax. Вместо того, чтобы управляться переходами по страницам, Flex-приложения управляются событиями. При том, что визуальные элементы Flex-приложения могут оставаться неизменными, в то же время само приложение может совершать запросы и получать ответы. Поэтому обмен данными во Flex определенно требует отойти от тех стратегий, которые использовались в управляемых переходами по страницам приложениях.
Фреймворк Flex включает компоненты для работы с с обменом данными, используя стандарт http-запросов, так же как и SOAP-запросы. Эти компоненты оказываются полезны тогда, когда используется первая из общих стратегий обмена данными: размещение кода (компонента), который делает запрос, внутри класса или MXML-документа, который оперирует данными. Часто это наиболее очевидный подход, но часто это такая стратегия, которая плохо масштабируется. Такой подход ведет к децентрализации обмена данными, что вызывает некоторые проблемы:
– Управление обмена данными затруднено, когда код де централизован, просто потому что это такой код трудно все время отыскивать.
– Когда обмен данными плотно связан с отдельными визуальными объектами, которые используют данные, такие данные не доступны для чтения из других частей приложения. Это может не казаться проблемой, пока вы не примите во внимание то, что многие приложения используют одни и те же данные в разных местах, и если вы разместите код обмена данными внутри визуальных элементов, которые используют эти данные, вы сделаете затруднительным синхронизацию данных, и вам возможно потребуется перезапрашивать одни и те же данные по многу раз.
– Децентрализация кода обмена данными делает приложение хрупким, потому что какое-либо изменение в процессе обмена данными (протокола, API и т.п.) может нарушить работоспособность приложения в разных местах. Напротив, когда код обмена данными централизован, относительно легко адаптировать приложение при изменении чего-либо в процессе обмена данными.
Хотя первая стратегия имеет такие значимые и свойственные ей ловушки, мы все же вернемся к обсуждению соответствующих компонентов в этой главе, поскольку эта стратегия не лишена преимуществ. Компоненты часто предоставляют более быстрый способ собрать приложение, готовое к обмену данными. Это полезно в случаях быстрого прототипирования, тестовых приложений и слабо-масштабируемых (некрупных) приложений со сниженными техническими требованиями.
Вторая стратегия требует централизировать обмен данными, используя объекты прокси. Прокси – это объекты, которые «живут» внутри клиента на уровне, где они могут поддерживать соединение с отдаленными сервисами. Объекты прокси могут даже иметь те же API, что и отдаленные сервисы. Отдаленные прокси обеспечивают централизацию для кода обмена данными, и они скрывают детали того, как осуществляется обмен данными от остальных частей приложения. Даже если изменится реализация обмена, остальное приложение может спокойно продолжать делать вызовы объектов прокси.
(Комментарий переводчика. — В тексте используется термин remote proxy. В данном случае имеется в виду вовсе не прокси-сервера в интернете, то есть, отдельные машины, пропускающие через себя трафик и имеющие собственный IP-адрес. Термин Remote Proxy встречается в шаблонах проектирования и означает примерно следующее: это локальный заместитель реального объекта, находящегося в другом адресном пространстве, например, объекта, работающего на другом хосте. Клиент полагает, что он общается с удаленным объектом, но на самом деле между ними располагается прокси. Прокси транслирует запросы клиента в удаленные вызовы, передает их удаленному объекту, получает от него ответ и возвращает его клиенту. )
Вторая стратегия намного более масштабируема, чем первая. Кроме того, поскольку код обмена данными централизован, эта стратегия не восприимчива к некоторым проблемам первой стратегии, таким как дублирование запросов данных, проблемы синхронизации и адаптируемости. По этим причинам, мы настойчиво рекомендуем использовать прокси-подход для коммерческих приложений.
Работа в режиме обмена данными по принципу Запрос/Ответ.
Вы можете работать с режимом запроса-получения данных тремя основными способами: через простые HTTP-сервисы, веб-сервисы, а также Flash Remoting. Каждый способ преследует одну и ту же основную цель отсылки запроса и получения ответа, и по существу, вы можете использовать их для все тех же целей Flex-приложений. Какой способ выбрать, зависит в основном от того, какой тип сервиса вам доступен. Например, если вы загружаете данные XML из XML-документа, то лучше использовать простое соединение по HTTP. Однако, если вы захотите вызывать методы веб-сервиса, имеет смысл использовать обмен с веб-сервисами.
Простые HTTP-Сервисы
Самый базовый тип HTTP-обмена по принципу запрос/ответ использует то, что мы называем простыми HTTP-сервисами. Эти сервисы включают в себя такие вещи, как текстовые и XML ресурсы, а также статичные или динамические документы (страницы), генерируемые такими системами как ColdFusion, сервлетами или ASP.NET. Простые HTTP-сервисы могут также включать в себя запросы страниц, которые запускают скрипты для выполнения некоторых операций, таких как вставка данных в базу данных или обновление записей, или отсылка почты. Вы можете использовать простые HTTP-запросы чтобы выполнить подобного рода действия на стороне сервера, или запросить данные, либо и то, и другое одновременно.
Flex предоставляет два основных пути, которыми вы можете обратиться к простому HTTP-сервису: использование HTTPService, компонента фреймворка Flex, либо задействовать класс Flash-плеера flash.net.URLLoader.
HTTPService
HTTPService – это компонент, который позволяет вам делать запросы к простым HTTP сервисам, таким как текст, XML файлы, или скрипты и страницы, возвращающие динамические данные. Вы должны всегда определять значение свойства url объекта HTTPService. Свойство url указывает объекту, где находится ресурс, к которому следует отправить запрос. Значением может быть как относительный адрес URL, так и абсолютный. В следующем примере используется MXML для создания объекта HTTPService, который загружает текст из файла с именем data.txt, сохраненного в той же директории, что и скомпилированный swf файл.
<mx:HTTPService id="textService" url="data.txt" />
Итак, вы узнали как создавать новый экземпляр HTTPService. Давайте теперь рассмотрим, как отправлять запросы, получать ответы и передавать параметры.
Отправка запросов
Создание объекта HTTPService не означает автоматического выполнения запроса на загрузку данных. Для того, чтобы выполнить запрос, нужно вызвать метод send(). Вы можете вызвать метод send() в ответ на любое внутреннее или пользовательское событие. Например, если вы хотите выполнить запрос сразу же после инициализации, вы можете вызвать send() в обработчике initialize. Если вам нужно, чтобы данные загружались после клика на кнопку, вы можете вызывать метод send() в обработчике события click: textService.send( );
Получение ответа
Метод send() совершает запрос, но ответ не всегда приходит мгновенно. Напротив, приложение должно ожидать результата. Событие получения результата (result) срабатывает когда ответ поступил полностью. Следующий пример отображает окошко с сообщением после того, как данные загрузятся:
<mx:HTTPService id="textService" url="data.txt" result="mx.controls.Alert.show('Data loaded')" />
Конечно же, скорее всего вам потребуется сделать что-то более полезное, чем просто вывести сообщение после загрузки данных. Как правило, вам нужно использовать эти данные каким-либо образом. Вы можете извлечь полученные данные, воспользовавшись свойством lastResult. Неформатированный текст всегда загружается как строковый тип данных. Однако, компонент HTTPService способен автоматически конвертировать упорядоченные данные в ассоциированный массив. По этой причине свойство lastResult относится к типу Object. Если вы захотите получить значение как строку, выполните привидение типа. Пример 16-1 загружает тест из файла и отображает в текстовом поле.
Пример 16-1. Загрузка текста компонентом HTTPService
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:remoting="com.oreilly.
programmingflex.rpc.*" layout="absolute" initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
textService.send( );
}
private function resultHandler(event:Event):void {
textArea.text = String(textService.lastResult);
}
]]>
</mx:Script>
<mx:HTTPService id="textService" url="data.txt" result="resultHandler(event)" />
<mx:TextArea id="textArea" />
</mx:Application>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:remoting="com.oreilly.
programmingflex.rpc.*" layout="absolute" initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
textService.send( );
}
private function resultHandler(event:Event):void {
textArea.text = String(textService.lastResult);
}
]]>
</mx:Script>
<mx:HTTPService id="textService" url="data.txt" result="resultHandler(event)" />
<mx:TextArea id="textArea" />
</mx:Application>
Хотя вы можете детально обрабатывать событие получения результата, обычно все же используют механизм биндинга. Пример 16-2 реализует то же самое, что и Пример 16-1, но с использованием биндинга.
Пример 16-2. Использование механизма биндинга в сочетании с HTTPService
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:remoting="com.oreilly.
programmingflex.rpc.*" layout="absolute" initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
textService.send( );
}
]]>
</mx:Script>
<mx:HTTPService id="textService" url="data.txt" />
<mx:TextArea id="textArea" text="{textService.lastResult}" />
</mx:Application>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:remoting="com.oreilly.
programmingflex.rpc.*" layout="absolute" initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
textService.send( );
}
]]>
</mx:Script>
<mx:HTTPService id="textService" url="data.txt" />
<mx:TextArea id="textArea" text="{textService.lastResult}" />
</mx:Application>
Когда это возможно, HTTPService разбирает данные, которые он загрузил, примерно так же как это делается с данными, размещенными внутри тэга Model. Например, посмотрите на следующий блок данных:
<countries>
<country>Select One</country>
<country>Canada</country>
<country>U.S.</country>
</countries>
<country>Select One</country>
<country>Canada</country>
<country>U.S.</country>
</countries>
Если вы попробуете загрузить эти данные, используя компонент HTTPService, они будут разобраны в объект с названием countries, который будет содержать массив country, каждый элемент которого будет взят из значений, обрамленных тэгами
Пример 16-3. Загрузка XML компонентом HTTPService
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
countriesService.send( );
}
]]>
</mx:Script>
<mx:HTTPService id="countriesService" url="http://www.rightactionscript.com/state s/xml/countries.xml" />
<mx:VBox>
<mx:ComboBox id="country" dataProvider="{countriesService.lastResult.countries.c ountry}" />
</mx:VBox>
</mx:Application>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
countriesService.send( );
}
]]>
</mx:Script>
<mx:HTTPService id="countriesService" url="http://www.rightactionscript.com/state
<mx:VBox>
<mx:ComboBox id="country" dataProvider="{countriesService.lastResult.countries.c
</mx:VBox>
</mx:Application>
Как вы уже узнали, по умолчанию, HTTPService интерпретирует результаты как текст, если они являются блоком текста, или парсит их в объект, если результатом будут XML-данные. Однако, это поведение по умолчанию. Вы можете жестко задать способ, которым будет обработан результат, с помощью свойства resultFormat все того же объекта HTTPService. Значение по умолчанию — object, что означает описанное выше поведение. Вы можете также использовать любое из следующих значений:
text
Данные никак не преобразуются, остаются как простой текст.
flashvars
Допускаются данные в URL-преобразованном формате (URL-encoded), и они будут разделены на пары имя/значение как поля объекта.
array
Ожидаются данные в XML-формате, которые будут преобразованы в объект по тому же принципу, что и при значении object. Однако, в этом случае, результатом всегда будет массив. Если возвращаемые данные не могут быть автоматически преобразованы (разбиты) в массив, массив все равно будет создан, и результат будет помещен в него в качестве первого элемента.
xml
Допускаются данные в формате XML, причем разбор XML осуществляется на основе ActionScript-класса XMLNode.
e4x
Допускаются данные в формате XML, причем XML парсится на основе ActionScript3-класса XML (E4X).
Отсылка параметров
Когда вам требуется передать сервису параметры, вы можете использовать свойство request объекта HTTPService. Свойство request принимает значения типа Object. По умолчанию, пары имя/значение конвертируются в формат URL-encoded и отсылаются сервису через HTTP GET. Вы можете создать такой объект в ActionScript, как показано здесь:
var parameters:Object = new Object( );
parameters.a = "one";
parameters.b = "two";
service.request = parameters;
parameters.a = "one";
parameters.b = "two";
service.request = parameters;
Однако, создавая объект HTTPService в MXML, удобно объявлять параметры также в MXML:
<mx:HTTPService id="service" url="script.php">
<mx:request>
<a>one</a>
<b>two</b>
</mx:request>
</mx:HTTPService>
<mx:request>
<a>one</a>
<b>two</b>
</mx:request>
</mx:HTTPService>
Объявление запроса таким способом позволит вам также использовать механизм биндинга с параметрами. Чтобы продемонстрировать это на работающем примере, рассмотрите код в Примере 16-4, который расширяет Пример 16-3 использованием второго объекта HTTPService для получения названий штатов соотвественно выбранной стране.
Пример 16-4. Использование HTTPService с входными параметрами
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
countriesService.send( );
}
private function changeHandler(event:Event):void {
statesService.send( );
}
]]>
</mx:Script>
<mx:HTTPService id="countriesService" url="http://www.rightactionscript.com/state s/xml/countries.xml" />
<mx:HTTPService id="statesService" url="http://www.rightactionscript.com/state s/xml/states.php">
<mx:request>
<country>{country.value}</country>
</mx:request>
</mx:HTTPService>
<mx:VBox>
<mx:ComboBox id="country" dataProvider="{countriesService.lastResult.countries.c ountry}" change="changeHandler(event)" />
<mx:ComboBox dataProvider="{statesService.lastResult.states.state} " />
</mx:VBox>
</mx:Application>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="initializeHandler(event)">
<mx:Script>
<![CDATA[
private function initializeHandler(event:Event):void {
countriesService.send( );
}
private function changeHandler(event:Event):void {
statesService.send( );
}
]]>
</mx:Script>
<mx:HTTPService id="countriesService" url="http://www.rightactionscript.com/state
<mx:HTTPService id="statesService" url="http://www.rightactionscript.com/state
<mx:request>
<country>{country.value}</country>
</mx:request>
</mx:HTTPService>
<mx:VBox>
<mx:ComboBox id="country" dataProvider="{countriesService.lastResult.countries.c
<mx:ComboBox dataProvider="{statesService.lastResult.states.state}
</mx:VBox>
</mx:Application>
В примере выше первый список заполняется странами. Когда пользователь выбирает страну из выпадающего списка, отсылается запрос второму сервису, PHP-скрипту, с выбранной страной в качестве параметра. Данные возвращаются в таком формате:
<states>
<state>Alabama</state>
<state>Alaska</state>
<!-- etc. -->
</states>
<state>Alabama</state>
<state>Alaska</state>
<!-- etc. -->
</states>
Как уже говорилось, по молчанию, параметры отсылаются URL-кодированном формате, используя HTTP GET. Однако, вы можете поменять эти установки. Свойство contentType объекта HTTPService задает формат, в котором отправляется контент. Стандартно задан формат application/x-www-form-urlencoded, который отправляет значения в формате URL-encoded. Вы можете определить формат application/xml, чтобы отсылать данные как XML, если сервис ожидает неформатированный XML:
<mx:HTTPService id="service" url="script.php" contentType="application/xml">
<mx:request>
<parameters>
<a>one</a>
<b>two</b>
</parameters>
</mx:request>
</mx:HTTPService>
<mx:request>
<parameters>
<a>one</a>
<b>two</b>
</parameters>
</mx:request>
</mx:HTTPService>
Свойство method задает, какой способ передачи будет использован. По умолчанию, это GET, но вы также можете определить значение как POST, HEAD, OPTIONS, PUT, TRACE, или DELETE.
Продолжение здесь