Tracking it down
At first I thought this was a coercion bug where null
was getting coerced to "null"
and a test of "null" == null
was passing. It's not. I was close, but so very, very wrong. Sorry about that!
I've since done lots of fiddling on wonderfl.net and tracing through the code in mx.rpc.xml.*
. At line 1795 of XMLEncoder
(in the 3.5 source), in setValue
, all of the XMLEncoding boils down to
currentChild.appendChild(xmlSpecialCharsFilter(Object(value)));
which is essentially the same as:
currentChild.appendChild("null");
This code, according to my original fiddle, returns an empty XML element. But why?
Cause
According to commenter Justin Mclean on bug report FLEX-33664, the following is the culprit (see last two tests in my fiddle which verify this):
var thisIsNotNull:XML = <root>null</root>;
if(thisIsNotNull == null){
// always branches here, as (thisIsNotNull == null) strangely returns true
// despite the fact that thisIsNotNull is a valid instance of type XML
}
When currentChild.appendChild
is passed the string "null"
, it first converts it to a root XML element with text null
, and then tests that element against the null literal. This is a weak equality test, so either the XML containing null is coerced to the null type, or the null type is coerced to a root xml element containing the string "null", and the test passes where it arguably should fail. One fix might be to always use strict equality tests when checking XML (or anything, really) for "nullness."
Solution
The only reasonable workaround I can think of, short of fixing this bug in every damn version of ActionScript, is to test fields for "null" and
escape them as CDATA values.
CDATA values are the most appropriate way to mutate an entire text value that would otherwise cause encoding/decoding problems. Hex encoding, for instance, is meant for individual characters. CDATA values are preferred when you're escaping the entire text of an element. The biggest reason for this is that it maintains human readability.
After reading your question, I would say, generate special token to do request required. This token will live in specific time (lets say in one day).
Here is an example from to generate authentication token:
(day * 10) + (month * 100) + (year (last 2 digits) * 1000)
for example:
3 June 2011
(3 * 10) + (6 * 100) + (11 * 1000) =
30 + 600 + 11000 = 11630
then concatenate with user password, example "my4wesomeP4ssword!"
11630my4wesomeP4ssword!
Then do MD5 of that string:
05a9d022d621b64096160683f3afe804
When do you call a request, always use this token,
https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata
This token is always unique everyday, so I guess this kind of protection is more than sufficient to always protect ur service.
Hope helps
:)
Best Solution
Versioning is a complex topic, so first, you need to define your goals in a more descriptive manner. It would be great to say that you have an interface insures you'll never break compatibility, but depending on what the new functionality is, that might not even be possible. So there are different situations and different trade-offs.
If your intent is to only provide new functionality to new consumers, and all of your consumers are direct consumers (no intermediaries, frameworks, etc.), then a discrete endpoint approach is the best choice. Each time you add a feature that risks a break, create a new endpoint, give it a new version number and then let the consumers know to validate against it and switch their configurations. This strategy is pretty tried and true, but it has the drawbacks of putting the burden on consumers to keep up to date. Also, if there are dependencies between services it can become a chore to track. The upside being if code breaks it's not (directly) your fault.
The other main strategy is the extensible interface. There's three different varieties here that I'm aware of. First, is the type of interface that tries to so well describe the service domain that every possible feature you might add is somehow possible given the existing interface. If that sounds hard, it is. You might call this the perfect interface. Everything is completely described, but the entire domain is also completely described. The "perfect" is really only on paper though.
The second variety is the type that looks like a normal interface but add generic extension points. In WSDLs this means xs:any, name-value pairs or something similar. You might call this the basic extensible interface. It's not too hard to do, but it's not without it's complications. The extension points may make the interface harder to work with in certain tooling (xs:any), or explicitly lose some of your ability to validate inputs and outputs (name-value pairs). It's also pretty easy to abuse those extension points in a way that makes the version 3 or 4 pretty hard to use.
The third variety is the type that converts your interface into a byte-stream. You might call these god interfaces. They aren't without their justifications, but if you're using one, you may want to ask why you're using web-services at all. Maybe you should be thinking about raw TCP/IP, or basic HTTP GET/POST. But maybe you're fed up with the complexity of WSDLs and XSDs and you just want to start from scratch but you're tied to web-services for some infrastructure reason. Realize however that once you start down this path, you're going to need a whole new way of describing to your consumers how to/not to use your service, and if you use XSD for that.. well you're basically back where you started.
Your best bet is to know all these options and approach your service design by first trying for the "perfect interface", then giving up and adding generic extensibility points. Trying to design the perfect interface will force you to learn things which will make your service better, not just your interface, but it will take time, and if you don't limit that time somehow, it'll take forever.
A little bit short of a true god interface, there is the wrapper interface. If you're system has layers, you want your interface to be in layers too. When you change layer B, you only want to change layer B, not all the instances in layer C.