I have a ColdFusion server with an HTTP API that's currently returning either JSON or XML formatted responses. Internally, all of these responses are represented using the ColdFusion 'struct' type, and then converted to the appropriate format before being presented to the requesting client.
The client team using this API has requested that in addition to JSON and XML, we also return AMF objects. Now, I've seen (and have myself made) arguments against using AMF as a 'returned format' in a REST scenario rather than as an RPC format. I'm not interested in comments about whether or not it's a good idea -- I'd just like to know if it will work. If not, I'm hoping for clear reasons why it won't. If it will work, I'd love some guidance on how to 'makeitgo'.
So, in the interest of achieving that proof of concept example, I'm trying to serialize a two-element ColdFusion Array using BlazeDS, then consume that serialized object in a Flash Player 10/AS3 test.
Here's what the code looks like on the server side:
//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();
Now, this generates some kind of binary data. If I stick that in a .cfm and try to load the page, I get something I can read in a hex editor that looks like it contains the array members I set. One note here: part of the response message includes "flex.messaging.io.ArrayCollection." I'm not knowledgeable enough to know what this tells me yet: anyone who can provide details about typing between the two environments will have many thanks from me.
The next step is to try and consume this on the FlashPlayer side. Here's what the stripped down AS3 looks like:
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
}
As indicated by the comment, this fails with error #2006 "Supplied index is out of bounds." I've searched around for common causes of this error, but haven't found any clear answers. I've tried resetting the byteArray.position to both the beginning and the end of the byteArray before attempting readObject() -- changing it to the end spits out error #2030 "End of file was encountered" (as one might expect), but I've verified that the .position defaults to 0, which generates the #2006 error.
I'm pretty sure that the issue here lies with the choice of BlazeDS calls I've used; I think I might be serializing a message when I want to be serializing an object. Unfortunately, the JavaDoc autogenerated docs for BlazeDS are ... less than enlightening. All of the more readable resources I've found focus on Flash Remoting and RPC examples. Surprising, I know; but it is what it is. I'm using Adobe BlazeDS docs; if anyone else has a better resource I'd be quite appreciative.
As a reminder to anyone answering: I realize this isn't a typical use of AMF. I'm not interested in responses that suggest the typical RPC method or to switch to Flash Remoting rather than HTTP/GET. What I need is an AMF serialized response from an HTTP request which can be deserialized on a Flash Player client. I don't have a choice in this matter. I do need to know if this is possible, and if so I'm hoping for some guidance on how to make it work. I welcome any and all suggestions aside from "Just don't use AMF!" or "Just switch to Flash Remoting!"
Update: made a little bit of progress with this:
1) on the server side, I used the ASObject class of blazeDS to create a simple ASObject and populate it with a key-value pair. 2) on both the client and server side, I had to make sure to set the object encoding to AMF0. The same technique in AMF3 generates that #2006/Out of bounds error, and I'm not yet sure why.Here's what the code now looks like on the server-side:
//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();
The primary difference here is that rather than trying to serialize a CF Array, I'm building a response object (of type ASObject) manually.
On the client side, the code now looks like this:
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
}
The difference here is that I've explicitly set the ObjectEncoding to AMF0 (defaults to AMF3).
If I switch the objectEncoding to AMF3 on both server and client, I'd expect things to work, but I still get the 2006: out of bounds error. The ByteArray.length property i开发者_JAVA百科s the same in both the AMF0 and AMF3 cases, but the content of the returned object is different (when viewed in a hex editor).
Changing the objectEncoding in the first example I provided had no effect on the error that was being produced.
So, then, the issue seems to have been an attempt to serialize the ColdFusion array: the AMFSerializer doesn't know how to handle it. It needs to explicitly be built as an ASObject. I'll build a sanitize function to do the conversion between the two types.
I feel like progress has been made (and thanks for all the feedback in comments and answers), but I've still got a lot of unanswered questions. Does anyone have any input on why this might be failing when I try to encode in AMF3, but not for AMF0? I don't have any attachment to one or the other, but I don't like this 'throw things at the wall and see which ones stick' method of solving the problem... I'd like to know why it's failing =/
I did that a some time ago..you can check my blog post from here, maybe it can help you. I was using Java on the server side, not CF.
精彩评论