There are several custom SerializationFilters that can make your life easier (requires Flex 4).
Composite design pattern implementation for HTTP serialization filters. Override any of 5 SerializationFilter’s methods using different filter instances:
<mx:HTTPService>
<mx:serializationFilter>
<rest:CompositeSerializationFilter>
<!-- Serialize requests in JSON -->
<rest:bodySerializer>
<rest:JSONSerializationFilter />
</rest:bodySerializer>
<!-- Deserialize replies in JSON -->
<rest:resultDeserializer>
<rest:JSONSerializationFilter />
</rest:resultDeserializer>
<!-- Build REST-style URLs dynamically -->
<rest:urlSerializer>
<rest:RESTSerializationFilter />
</rest:urlSerializer>
<!-- Use custom HTTP methods -->
<rest:parametersSerializer>
<rest:RESTSerializationFilter />
</rest:parametersSerializer>
<!-- Set request content type to application/json -->
<rest:contentTypeProvider>
<rest:RESTSerializationFilter requestContentType="application/json" />
</rest:contentTypeProvider>
</rest:CompositeSerializationFilter>
</mx:serializationFilter>
</mx:HTTPMultiService>
SerializationFilter’s methods that can be overriden:
deserializeResult
getRequestContentType
serializeParameters
serializeBody
serializeURL
Methods that are not overridden are handled using a regular SerializationFilter.
Implements serializeParameters
, contentTypeProvider
and serializeURL
methods.
Allows you to sent custom HTTP verbs, other than GET or POST, which is not supported by the Flash Player by default. You can specify one of the three possible ways to do that:
- Send it in a X-HTTP-Method-Override header (
methodOverride="header"
). Don’t forget to update your crossdomain.xml file to allow headers to be sent to other domains. - Append a
_method
parameter to all URLs (methodOverride="url"
). - Send it a request body as a
_method
parameter (methodOverride="variable"
). This will force your request to be serialized asapplication/x-www-form-urlencoded
.
This serialization filter can also be used to build dynamic URLs that containt tokenized parameters (enclosed in square brackets).
<fx:Declarations>
<rest:RESTSerializationFilter
id="restSerializer"
methodOverride="variable"
requestContentType="application/x-www-form-urlencoded" />
<mx:HTTPMultiService
id="service"
baseURL="http://example.com/api/"
>
<mx:serializationFilter>
<!-- contentTypeProvider is required in order to convert custom HTTP verbs to POST
and to set content type to 'application/x-www-form-urlencoded'. -->
<!-- urlSerializer will replace square bracket tokens in URLs. -->
<!-- parametersSerializer will add a '_method' variable to your requests, if needed. -->
<rest:CompositeSerializationFilter
contentTypeProvider="{restSerializer}"
urlSerializer="{restSerializer}"
parametersSerializer="{restSerializer}" />
</mx:serializationFilter>
<mx:operationList>
<!--
[ID] and any other token will be automatically
replaced by variables from operation arguments object
-->
<mx:HTTPOperation
name="getUser"
url="User/[ID]" />
<!--
Request will be sent as a POST request
with a "_method"="DELETE" variable
-->
<mx:HTTPOperation
name="deleteUser"
url="User/[ID]"
method="DELETE" />
</mx:operationList>
</mx:HTTPMultiService>
</fx:Declarations>
import mx.rpc.AbstractOperation;
import mx.rpc.AsyncToken;
import vo.User;
private function getUser(userId:uint):AsyncToken
{
var operation:AbstractOperation = service.getOperation("getUser");
operation.arguments = { ID: userId }; //replace URL tokens
return operation.send();
//You can also add your variable to requests:
//return operation.send({ varname: "varvalue" });
}
Implements deserializeResult
, contentTypeProvider
and serializeBody
methods.
Serialize requests and deserialize replies in JSON with a fast native JSON parser (only in FP11 version; FP9 version is relying on as3corelib’s JSON parser). Requests and replies can be converted from and into strongly-typed objects on the fly. The following object mapping features are available:
- Incoming arrays can be mapped to strongly-typed vectors or arrays of typed objects.
ArrayElementType
metatag can be used to map arrays of typed objects.Transient
metatag is respected both in requests and responses.- Date strings in JSON format
/Date(1325376000000)/
can be converted toDate
objects, and vice versa.
For example, when you receive this:
{
"owner":{
"birthday":"/Date(1325376000000)/",
"firstName":"John",
"lastName":"Smith"
}
"wheels":[
{ "position": { "x":1, "y":1 } },
{ "position": { "x":1, "y":-1 } },
{ "position": { "x":-1, "y":1 } },
{ "position": { "x":-1, "y":-1 } }
]
}
It can be converted into these:
public class User
{
public var birthday:Date;
public var firstName:String;
public var lastName:String;
}
public class Car
{
public var owner:User;
[ArrayElementType("vo.Wheel")]
public var wheels:Array;
//You can just use vectors instead
//public var wheels:Vector.<Wheel>
}
import flash.geom.Point;
public class Wheel
{
public var position:Point;
}
Using the following code:
<fx:Declarations>
<mx:HTTPMultiService
id="service"
baseURL="http://example.com/api/"
>
<mx:serializationFilter>
<rest:CompositeSerializationFilter>
<!-- Serialize requests from objects to JSON. -->
<rest:bodySerializer>
<rest:JSONSerializationFilter />
</rest:bodySerializer>
<!-- Deserialize replies from JSON to strongly-typed objects. -->
<rest:resultDeserializer>
<rest:JSONSerializationFilter />
</rest:resultDeserializer>
<!-- Forces requests content type to be 'application/json'.
This is required in order to encode requests in JSON.
If content type is not changed either this way,
or manually on an operation, Flex will handle
request body encoding by itself. -->
<rest:contentTypeProvider>
<rest:RESTSerializationFilter requestContentType="application/json" />
</rest:contentTypeProvider>
<!-- This serializer is required for JSON requests. -->
<rest:parametersSerializer>
<rest:RESTSerializationFilter />
</rest:parametersSerializer>
</rest:CompositeSerializationFilter>
</mx:serializationFilter>
<mx:operationList>
<!--
Note that there is a resultElementType
property set on the operation
-->
<mx:HTTPOperation
name="getCar"
url="Car/[ID]"
method="GET"
resultElementType="{Car}"
result="onCarLoaded(event)" />
</mx:operationList>
</mx:HTTPMultiService>
</fx:Declarations>
import mx.rpc.events.ResultEvent;
import vo.Car;
import vo.User;
import vo.Wheel;
private function onCarLoaded(event:ResultEvent):void
{
var car:Car = Car(event.result);
var wheels:Vector.<Wheel> = car.wheels;
var owner:User = car.owner;
var birthday:Date = owner.birthday;
}
If you have an array of objects…
[
{ "x":1, "y":5 },
{ "x":2, "y":4 },
{ "x":3, "y":3 },
{ "x":4, "y":2 },
{ "x":5, "y":1 }
]
…you can receive it as an array of strongly-typed objects by setting
resultFormat="array"
and resultElementType="{ElementClass}"
. By default, all arrays are wrapped into an ArrayCollection
. To avoid this behavior, just set makeObjectsBindable="false"
on the operation instance.
<mx:HTTPOperation
name="getPoints"
url="Points/[FROM]/[TO]"
resultFormat="array"
makeObjectsBindable="false"
resultElementType="{Point}"
result="onPointsLoaded(event)" />
private function onPointsLoaded(event:ResultEvent):void
{
trace(event.result.length);
for each(var point:Point in event.result)
trace(point.x + point.y);
}
JSONSerializationFilter
is an implementation of an abstract TypedSerializationFilter
that handles types transformation, while a concrete child class handles actual data serialization and deserialization.
Setting resultFormat
to any other value, besides array
and object
(default) will result in a JSONSerializationFilter
(or any other TypedSerializationFilter
) to be suppressed in a selected HTTPOperation
.
Implements deserializeResult
, contentTypeProvider
and serializeBody
methods.
Acts the same way as the JSONSerializationFilter
. You can use the optional xmlEncode
and xmlDecode
properties to assign custom serialization methods:
<mx:HTTPService>
<mx:serializationFilter>
<rest:CompositeSerializationFilter>
<!-- Serialize requests from objects to XML. -->
<rest:bodySerializer>
<rest:XMLSerializationFilter xmlEncode="{doXmlEncode}" />
</rest:bodySerializer>
<!-- Deserialize replies from XML to strongly-typed objects. -->
<rest:resultDeserializer>
<rest:XMLSerializationFilter xmlDecode="{doXmlDecode}" />
</rest:resultDeserializer>
<!-- Forces requests content type to be 'application/xml'. -->
<rest:contentTypeProvider>
<rest:RESTSerializationFilter requestContentType="application/xml" />
</rest:contentTypeProvider>
<!-- This serializer is required for XML requests. -->
<rest:parametersSerializer>
<rest:RESTSerializationFilter />
</rest:parametersSerializer>
</rest:CompositeSerializationFilter>
</mx:serializationFilter>
</mx:HTTPMultiService>
private function doXmlEncode(body:Object):XMLNode
{
return new XMLNode(1, '<body id="' + body.id + '"/>');
}
private function doXmlDecode(node:XMLNode):Object
{
return { id: node.attributes.id };
}
Extends the CompositeSerializationFilter
with two new properties: username
and password
. Will add a basic HTTP authentication header to all requests.
<mx:HTTPService>
<mx:serializationFilter>
<rest:BasicAuthenticationSerializationFilter username="admin" password="qwerty">
<rest:bodySerializer>
<rest:XMLSerializationFilter />
</rest:bodySerializer>
<rest:resultDeserializer>
<rest:XMLSerializationFilter />
</rest:resultDeserializer>
<rest:contentTypeProvider>
<rest:RESTSerializationFilter requestContentType="application/xml" />
</rest:contentTypeProvider>
<rest:parametersSerializer>
<rest:RESTSerializationFilter />
</rest:parametersSerializer>
</rest:BasicAuthenticationSerializationFilter>
</mx:serializationFilter>
</mx:HTTPMultiService>
Extends the CompositeSerializationFilter
. Can be used to create and assign an OAuth header to your requests.
<mx:HTTPService>
<mx:serializationFilter>
<rest:OAuthSerializationFilter id="oauthFilter" keySecret="kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw" tokenSecret="LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE">
<rest:bodySerializer>
<rest:XMLSerializationFilter />
</rest:bodySerializer>
<rest:resultDeserializer>
<rest:XMLSerializationFilter />
</rest:resultDeserializer>
<rest:contentTypeProvider>
<rest:RESTSerializationFilter requestContentType="application/xml" />
</rest:contentTypeProvider>
<rest:parametersSerializer>
<rest:RESTSerializationFilter />
</rest:parametersSerializer>
</rest:BasicAuthenticationSerializationFilter>
</mx:serializationFilter>
<mx:operationList>
<mx:HTTPOperation
name="getUser"
url="User/[ID]" />
</mx:operationList>
</mx:HTTPMultiService>
import mx.rpc.AbstractOperation;
import mx.rpc.AsyncToken;
import mx.rpc.http.OAuthHeader;
import mx.rpc.utils.OAuthUtil;
import vo.User;
private function getUser(userId:uint):AsyncToken
{
var oauthData:OAuthHeader = new OAuthHeader();
oauthData.oauth_version = "1.0";
oauthData.oauth_consumer_key = "xvz1evFS4wEEPTGEFPHBog";
oauthData.oauth_token = "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb";
oauthData.oauth_nonce = OAuthUtil.generateNonce();
oauthData.oauth_signature_method = OAuthHeader.SIGNATURE_METHOD_HMAC_SHA1;
oauthData.oauth_timestamp = OAuthUtil.getTimestamp();
var operation:AbstractOperation = service.getOperation("getUser");
operation.arguments = { ID: userId }; //replace URL tokens
oauthFilter.oauthData = oauthData;
return operation.send();
}
Static mx.utils.TypeUtil
class can be used for all type castings listed above.
mx.rpc.OperationResponder
can be used to make operation handlers more straightforward. It mimics Flex’s AsyncResponder
class, but with a different result/fault functions signatures. A valid result handler signature is function(valueObject:Object):void
. A result handler function will receive ResultEvent.result
value, so you can just set your result type in a handler: function(valueObject:MyResultClass):void
. A valid fault handler signature is function(fault:Fault):void
.
import mx.rpc.OperationResponder;
import vo.User;
private function getUser():void
{
service.getOperation("getUser").send().addResponder(new OperationResponder(onResult, onFault));
}
private function onResult(user:User):void
{
trace(user.firstName);
}
private function onFault(fault:Fault):void
{
trace(fault.faultDetail);
}