Pregunta

I have an implementation of SWF Object to modify the XHR object for Cross domain requests for IE.

Once the object is available I modify ther XHR like this:

if ($.browser.msie) {
    amplify.subscribe("request.before.ajax", function (resource, settings, ajaxSettings, xhr) {
        if (resource.url.indexOf(requestConfig.apiUrl) != -1) {
            xhr.setRequestHeader("Accept", "application/xml, text/xml");
            var shr = new SWFHttpRequest();                
            ajaxSettings.xhr = function () {
                return shr;
            };
            ajaxSettings.crossDomain = false;
            ajaxSettings.error = function (e) {
                // Returns the data object I expect:
                alert(e.responseText);
            }
        }
    });
};

my jQuery ajaxError thrown error is: SyntaxError: Invalid character

  • The error is caused by a backslash:

When in my JSON I have for example: "Username" : "DOM\\PRO4" it somehow becomes "DOM\PRO4" to the browser and gives the Syntax error. But the server is escaping as I can see the raw data sent from the server has \ but by some reason that I can understand flash is probably breaking that.

enter image description here

enter image description here

Response Body from IE is:

{
"ResponseCode": 200,
"ResponseDescription": "OK",
"ResponseEntity": [
    {
        "Id": "6d0d9471-f118-4984-9182-a20300a037b6",
        "Name": "DYN",
        "ConnectionTypeId": "20000000-0000-0000-0003-000000000003",
        "ConnectionTypeName": null,
        "Address": "http://54.229.51.444:7777/CRMServer",
        "UserName": "DOM\\PRO4",
        "Password": "",
        "AddedById": "20000000-0000-0000-0004-000000000001",
        "DateAdded": "07/22/2013 09:43:20",
        "DateModified": "22/07/2013 15:00:01",
        "ModifiedById": "",
        "TypeName": "Dynamics"
    }
]
}

Which you can see Username is correctly escaped but thr ajax settings error alert shows the username in the responseText with only 1 backslash.

and if you also need to look at the SWF object, its a modified version of an open source module developed using haxe:

import flash.external.ExternalInterface;
import flash.Lib;
import haxe.Http;
import flash.events.Event;
import flash.external.ExternalInterface;
import flash.display.Sprite;

class SWFHttpRequest {

    public static var ver:String = '0.3';

    public static var transports:Map<String,Transport> = new Map();

    public static function abort( instance:Int ) {
        transports.get( instance + '' ).abort();
    }

    public static function open( instance:Int, method:String, url:String ) {
        transports.set( instance + '', new Transport( instance, method, url ) );
    }

    public static function send( instance:Int, data:String ) {
        transports.get( instance + '' ).send( data );
    }

    public static function setRequestHeader( instance:Int, header:String, value:String ) {
        transports.get( instance + '' ).setRequestHeader( header, value );
    }

    public static function version() {
        return ver;
    }

    private static function onLoaded(event:Event):Void
    {
        flash.external.ExternalInterface.call("swfHttpLoaded");
    }

    public static function main() {
        flash.system.Security.allowDomain("*");     
        ExternalInterface.addCallback("abort",abort);
        ExternalInterface.addCallback("open",open);
        ExternalInterface.addCallback("send",send);
        ExternalInterface.addCallback("setRequestHeader",setRequestHeader);
        ExternalInterface.addCallback("SWFHttpRequestVersion",version);
        ExternalInterface.call( [ "(function(){",
            "if (window.SWFHttpRequest) return;",
            "var Class = function(properties){",
                "var klass = function(){ return this.abort.apply(this); };",
                "klass.prototype = properties;",
                "klass.constructor = arguments.callee;",
                "return klass;",
            "};",
            "window.SWFHttpRequest = new Class({",
                "abort: function(){",
                    "if (typeof this.instance != 'undefined') {",
                        "window.SWFHttpRequest.instances[this.instance] = null;",
                        "window.SWFHttpRequest.engine.abort( this.instance );",
                    "}",
                    "this.readyState = 0;",
                    "this.responseText = '';",
                    "this.responseXML = null;",
                    "this.status = 0;",
                    "this.statusText = '';",
                    "this.instance = window.SWFHttpRequest.instances.length;",
                    "window.SWFHttpRequest.instances.push( this );",
                "},",
                "getAllResponseHeaders: function(){ return null; },",
                "getResponseHeader: function(){ return null; },",
                "onreadystatechange: null,",
                "open: function(method, url, async, user, password){",
                    "this.status = 0;",
                    "this.readyState = 1;",
                    "this.statusText = this.responseText = '';",
                    "this.responseXML = null;",
                    "window.SWFHttpRequest.engine.open( this.instance, method, url );",
                "},",
                "send: function(data){",
                    // TODO: Once haxe.Http supports setPostData() for flash9 targets, this function
                    // should be updated to allow for a Document as the data.  When that's the case,
                    // it should be serialized as XML prior to sending over to the Flash executor.
                    "window.SWFHttpRequest.engine.send( this.instance, data );",
                "},",
                "setRequestHeader: function(header, value){",
                    "if (this.readyState != 1 || !header || !value) return;",
                    "window.SWFHttpRequest.engine.setRequestHeader( this.instance, header, value );",
                "}",
            "});",
            "window.SWFHttpRequest.instances = [];",
            "window.SWFHttpRequest.version = '" + ver + "';",
            "var f = function(tag){",
                "var elems = document.getElementsByTagName(tag);",
                "for (var i=0; i<elems.length; i++) if (elems[i].SWFHttpRequestVersion) return elems[i];",
            "};",
            "window.SWFHttpRequest.engine = f('embed') || f('object');",
        "})" ].join('') );
        var params = Lib.current.loaderInfo.parameters;
        if (Reflect.hasField(params,'onload')) ExternalInterface.call( Reflect.field(params,'onload') );
        Lib.current.loaderInfo.addEventListener(Event.COMPLETE, onLoaded);
    }
}

class Transport {

    var active:Bool;
    var instance:Int;
    var method:String;
    var url:String;
    var http:Http;

    public function new( instance:Int, method:String, url:String ) {
        this.active = true;
        this.instance = instance;
        this.method = method.toUpperCase();
        this.url = url;
        this.http = new Http( this.url );
        this.http.onData = this.onData;
        this.http.onError = this.onError;
        this.http.onStatus = this.onStatus;     
    }

    public function send( ?data:String ) {
        if ( data==null ) return this.http.request( this.method=='POST' );
        // NOTE: Once haxe.Http supports the setPostData() method for flash9 targets,
        // all the following should be replaced by: this.http.setPostData( data );
        // var re = ~/^(.*?)=(.*)$/;
        // var pairs:Array<String> = data.split('&');
        // var ud = function(s){
            // try { return StringTools.urlDecode(s); }
            // catch ( e:Dynamic ) { }
            // return s;
        // };
        // for( i in 0...pairs.length ) {
            // if (re.match(pairs[i])) this.http.setParameter( ud(re.matched(1)), ud(re.matched(2)) );
            // else this.http.setParameter( ud(pairs[i]), '' );
        // }
        this.http.setPostData( data );
        return this.http.request( this.method=='POST' );
    }

    public function setRequestHeader( header:String, value:String ) {
        this.http.setHeader( header, value );
    }

    public function onData( data:String ) {
        if (!this.active) return;
        ExternalInterface.call( [ "(function(instance, data){",
            "var shr = window.SWFHttpRequest.instances[instance];",
            "if (!shr) return;",
            "shr.status = 200;",
            "shr.statusText = 'OK';",
            "shr.readyState = 4;",
            "shr.responseText = data;",
            "try {",
                "if (window.DOMParser) {",
                    "var dp = new DOMParser();",
                    "shr.responseXML = dp.parseFromString( data, 'text/xml' );",
                "} else {",
                    "shr.responseXML = new ActiveXObject('Microsoft.XMLDOM');",
                    "shr.responseXML.async = 'false';",
                    "shr.responseXML.loadXML(data);",
                "}",
            "} catch(error) { shr.responseXML = null; }",
            "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();",
        "})" ].join(''), this.instance, data);
    }

    public function onError( msg:String ) {
        if (!this.active) return;
        ExternalInterface.call( [ "(function(instance){",
            "var shr = window.SWFHttpRequest.instances[instance];",
            "if (!shr) return;",
            "shr.status = 404;",
            "shr.statusText = 'Not Found';",
            "shr.readyState = 4;",
            "shr.responseText = null;",
            "shr.responseXML = null;",
            "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();",
        "})" ].join(''), this.instance );
    }

    public function onStatus( status:Int ) {
        if (!this.active || status==200) return;
        ExternalInterface.call( [ "(function(instance, status){",
            "var shr = window.SWFHttpRequest.instances[instance];",
            "if (!shr) return;",
            "shr.status = status;",
            "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();",
        "})" ].join(''), this.instance, status );
    }

    public function abort() {
        this.active = false;
    }

}

//
¿Fue útil?

Solución 2

in public function onData( data:String ) data doesn't have correct escaping.

after modifying this to be the following now things work as expected:

public function onData( data:String ) {
    if (!this.active) return;
    var stringified = haxe.Json.stringify(haxe.Json.parse(data));
    var stringifiedEscaped = StringTools.replace(stringified, "\\", "\\\\");
    ExternalInterface.call( [ "(function(instance, data){",
        "var shr = window.SWFHttpRequest.instances[instance];",
        "if (!shr) return;",
        "shr.status = 200;",
        "shr.statusText = 'OK';",
        "shr.readyState = 4;",
        "shr.responseText = data;",
        "try {",
            "if (window.DOMParser) {",
                "var dp = new DOMParser();",
                "shr.responseXML = dp.parseFromString( data, 'text/xml' );",
            "} else {",
                "shr.responseXML = new ActiveXObject('Microsoft.XMLDOM');",
                "shr.responseXML.async = 'false';",
                "shr.responseXML.loadXML(data);",
            "}",
        "} catch(error) { shr.responseXML = null; }",
        "if (shr.onreadystatechange && typeof shr.onreadystatechange=='function') shr.onreadystatechange();",
    "})" ].join(''), this.instance, stringifiedEscaped);
}

Otros consejos

You don't need to use Flash for cross-domain XHR anymore, even in IE. Judging by your images, CORS headers are already enabled.

All major browsers and IE 10+ have native support for cross-domain requests. Prior versions of IE support the XDomainRequest object which checks the Access-Control-Allow-Origin header (partial CORS support).

So unless you really need to support IE 7 or below, use regular XMLHttpRequest and XDomainRequest.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top