У меня есть сервер 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?У меня нет привязанности к одному или другому, но мне не нравится этот метод «бросить вещи в стену и посмотреть, какие из них придерживаются», метод решения проблемы ... Я хотел бы знать почему не получается = /