AMF как формат REST с использованием BlazeDS и URLLoader AS3 - PullRequest
3 голосов
/ 19 апреля 2011

У меня есть сервер ColdFusion с HTTP API, который в настоящее время возвращает ответы в формате JSON или XML. Внутри все эти ответы представлены с использованием типа struct ColdFusion, а затем преобразованы в соответствующий формат перед представлением запрашивающему клиенту.

Команда клиента, использующая этот API, запросила, чтобы в дополнение к JSON и XML мы также возвращали объекты AMF. Теперь я видел (и сам сделал) аргументы против использования AMF в качестве «возвращаемого формата» в сценарии REST, а не в качестве формата RPC. Меня не интересуют комментарии о том, хорошая это идея или нет - я просто хотел бы знать, сработает ли она . Если нет, я надеюсь по понятным причинам, почему это не так. Если это сработает, я хотел бы получить несколько советов о том, как «сделать».

Итак, в интересах достижения этого примера доказательства концепции я пытаюсь сериализовать двухэлементный массив ColdFusion с использованием BlazeDS, а затем использовать этот сериализованный объект в тесте Flash Player 10 / AS3.

Вот как выглядит код на стороне сервера:

//the test object I'm trying to return (keeping it simple)
var testArray = ArrayNew(1); 
testArray[1]="test"; 
testArray[2]="monkey";

//set up output stream to requesting client
var pc       = getPageContext();
var response = pc.getResponse();
var out      = response.getOutputStream();

response.setHeader("Content-Type", "application/x-amf");

//not sure if AmfMessageSerializer is the appropriate class to be using here
var amfMessageSerializer = createObject("java", "flex.messaging.io.amf.AmfMessageSerializer");  
var amfTrace             = createObject("java", "flex.messaging.io.amf.AmfTrace");  //needed by initialize() method

amfMessageSerializer.initialize(variables.SerializationContext.getSerializationContext(), out, amfTrace);

amfMessageSerializer.writeObject(testArray);

out.close(); 

Теперь это генерирует некоторый вид двоичных данных. Если я вставлю это в .cfm и попытаюсь загрузить страницу, я получу что-то, что смогу прочитать в шестнадцатеричном редакторе, который выглядит так, как будто он содержит элементы массива, которые я установил. Одно замечание: часть ответного сообщения включает в себя «flex.messaging.io.ArrayCollection». Я еще недостаточно осведомлен, чтобы знать, что это мне говорит: любой, кто может предоставить подробную информацию о наборе текста между двумя средами, будет благодарен мне.

Следующий шаг - попытаться использовать его на стороне FlashPlayer. Вот как выглядит урезанный AS3:

myURLloader.dataFormat = URLLoaderDataFormat.BINARY;
myURLloader.addEventListener(Event.COMPLETE, completeHandler);
myURLloader.load(myURLRequest); 

function completeHandler( event : Event) : void
{   
    var serverResponse : ByteArray = new ByteArray(); 
    serverResponse = event.target.data; 

    //read the response into a generic object
    var responseObject : Object = new Object(); 
    responseObject = serverResponse.readObject();   //fails here: error #2006
}

Как указано в комментарии, это приводит к ошибке # 2006 «Поставленный индекс выходит за пределы». Я искал общие причины этой ошибки, но не нашел четких ответов. Я попытался сбросить byteArray.position как в начало, так и в конец byteArray перед попыткой readObject () - при изменении его на конец выдается ошибка # 2030 «Обнаружен конец файла» (как можно было ожидать), но я проверил, что .position по умолчанию равен 0, что генерирует ошибку # 2006.

Я почти уверен, что проблема здесь заключается в выборе вызовов BlazeDS, которые я использовал; Я думаю, что я могу сериализовать сообщение, когда я хочу сериализовать объект. К сожалению, автоматически сгенерированные документы JavaDoc для BlazeDS ... меньше, чем просвещение. Все более читаемые ресурсы, которые я нашел, сосредоточены на примерах Flash Remoting и RPC. Удивительно, я знаю; Но что есть, то есть. Я использую Adobe BlazeDS docs ; если у кого-то есть лучший ресурс, я был бы весьма благодарен.

Как напоминание любому, кто отвечает: я понимаю, что это не типичное использование AMF. Меня не интересуют ответы, в которых предлагается типичный метод RPC или переключение на Flash Remoting, а не HTTP / GET. Мне нужен сериализованный ответ AMF от HTTP-запроса, который можно десериализовать на клиенте Flash Player. У меня нет выбора в этом вопросе. Мне нужно нужно знать, возможно ли это, и если да, то я надеюсь получить некоторые рекомендации о том, как заставить это работать. Я приветствую любые предложения, кроме "Просто не используйте AMF!" или "Просто переключитесь на Flash Remoting!"

Обновление: достигнут небольшой прогресс:
1) на стороне сервера я использовал класс blazeDS ASObject, чтобы создать простой ASObject и заполнить его парой ключ-значение.
2) на стороне клиента и сервера я должен был установить кодировку объекта AMF0. Та же самая техника в AMF3 генерирует эту ошибку # 2006 / Out of bounds, и я пока не уверен, почему.

Вот как теперь выглядит код на стороне сервера:

//set up output stream to requesting client
var pc       = getPageContext();
var response = pc.getResponse();
var out      = response.getOutputStream();

response.setHeader("Content-Type", "application/x-amf");

//not sure if AmfMessageSerializer is the appropriate class to be using here
var amfMessageSerializer = createObject("java", "flex.messaging.io.amf.AmfMessageSerializer");  
amfMessageSerializer.setVersion(variables.MessageIOConstants.AMF0);

var amfTrace  = createObject("java", "flex.messaging.io.amf.AmfTrace");  //needed by initialize() method
amfMessageSerializer.initialize(variables.SerializationContext.getSerializationContext(), out, amfTrace); 

var ASObject = createObject("java", "flex.messaging.io.amf.ASObject"); 
ASObject.put("testKey", "testValue"); //simple key-value map to return to caller

amfMessageSerializer.writeObject(testArray);

out.close(); 

Основное отличие состоит в том, что вместо того, чтобы пытаться сериализовать массив CF, я создаю объект ответа (типа ASObject) вручную.

На стороне клиента код теперь выглядит следующим образом:

myURLloader.dataFormat = URLLoaderDataFormat.BINARY;
myURLloader.addEventListener(Event.COMPLETE, completeHandler);
myURLloader.load(myURLRequest); 

function completeHandler( event : Event) : void
{   
    var serverResponse : ByteArray = new ByteArray(); 
    serverResponse = event.target.data; 
    serverResponse.objectEncoding = ObjectEncoding.AMF0; //important

    //read the response into a generic object
    var responseObject : Object = new Object(); 
    responseObject = serverResponse.readObject(); 
    trace(responseObject.testKey);                       //displays "testValue", as expected
}

Разница в том, что я явно установил ObjectEncoding в AMF0 (по умолчаниюAMF3).

Если я переключу objectEncoding на AMF3 как на сервере, так и на клиенте, я ожидаю, что все будет работать, но я все еще получаю ошибку 2006: вне границ.Свойство ByteArray.length одинаково в случаях AMF0 и AMF3, но содержимое возвращаемого объекта отличается (при просмотре в шестнадцатеричном редакторе).

Изменение objectEncoding в первом приведенном мною примере никак не повлияло на возникающую ошибку.

Итак, проблема заключается в попытке сериализации массива ColdFusion: AMFSerializer не знает, как с этим справиться.Он должен быть явно создан как ASObject.Я создам функцию очистки, чтобы выполнить преобразование между двумя типами.

Я чувствую, что прогресс достигнут (и спасибо за все отзывы в комментариях и ответах), но у меня все еще остается много вопросов без ответов.Кто-нибудь есть какие-либо данные о том, почему это может произойти сбой при попытке кодирования в AMF3, но не для AMF0?У меня нет привязанности к одному или другому, но мне не нравится этот метод «бросить вещи в стену и посмотреть, какие из них придерживаются», метод решения проблемы ... Я хотел бы знать почему не получается = /

1 Ответ

3 голосов
/ 20 апреля 2011

Я сделал это некоторое время назад ... вы можете проверить мой пост в блоге со страницы здесь , возможно, он может вам помочь Я использовал Java на стороне сервера, а не CF.

...