Menu

Using cross domain Webservices

Help
2008-11-23
2013-04-02
  • Patricio Stegmann

    Hello,
    I want to use Clean Ajax, but need it to support crossdomain webservice calls. I already did some research, and there is a replacement for browsers XMLHttpRequest methods, based on Flash, called Flash XMLHttpRequest, allowing the flash, which has its own security model, to allow call of other domains than the webserver ones, based on a domains.xml file on the webserver, which allows listing of allowed domains to interact with.
    Is there a simple way to set the XMLHttpRequest stuff to use the Flash based one instead of the regular ? This would help a lot with integration on webservices.
    Thanks for any help.

     
    • Carlos Eduardo Goncalves

      Hi Patricio,

      Cross Domain XMLHttpRequest is a requirement wanted by many developers around the world, unfortunally we are restricted by browsers security model.
      There are some initiatives that aim to provide some specification that will address this issue properly. Nowadays there are 3 ways to acomplish it:

      - A proxy server: You will need some server infrastructure to forward your requests to an external domain.
      - Flash (ActionScript): It is not a standard, but a proprietary solution developed by Adobe.
      - JSONP: OnDemand JavaScript that requires server-side collaboration.

      If you want to create some solution that integrates XMLHttpRequest and Flash you can try <a href="http://weblogs.macromedia.com/flashjavascript">FlashJavascript</a>

      Best regards,

      Carlos Eduardo.

       
    • Patricio Stegmann

      Carlos,

      Thanks for your answer !

      Even though flash is proprietary, I think it is the easier way to go, as it exists on all platforms and is largely distributed everywhere. From my application point of view it is the unique choice, because I need to contact a Webservice running on localhost, so any server-side treatment may not work because of, say, NAT-ed client applications, or firewall problems.

      I slightly modified the code for Engine.js:

      Engine.buildRequest = function() {
        try {
          //var obj = (typeof XMLHttpRequest != "undefined") ? new XMLHttpRequest() : Engine.buildActiveX(Engine.ACTIVEX_XHR);       
          if (typeof SWFHttpRequest != "undefined")
          {
              obj = new SWFHttpRequest();
          }
          else if (typeof XMLHttpRequest != "undefined")
          {
              obj = new XMLHttpRequest();
          }
          else
          {
              obj = Engine.buildActiveX(Engine.ACTIVEX_XHR);
          }
          return obj;
        }
        catch(e) {
          Engine.reportException(null, e);
        }   
      };

      This makes use of SWFHttpRequest if it is defined. It is then using that. The problem is that I get a 'too much recursion' error on Firefox. I dont know why this is happening but maybe the internals of the Flash file are doing mess. Do you know if there is something existing using the FlashJavascript and the clean-ajax module so I dont have to re-write the wheel ?

      Thanks for any help,

       
    • Patricio Stegmann

      Hi again !
      Well, I managed to do it with not lot of modifications. Basically I created a VirtualHttpRequest object, and added into your checker code. I had to change a bit of Engine.js:
      // ******************************
      // From file Engine.js
      // ******************************
      /**
      * <p>Build client side JavaScript objects required to XMLHTTP request.</p>   
      * @return
      *        Reference to a <code>XMLHttpRequest</code> or <code>ActiveXObject</code> instance.
      */
      Engine.buildRequest = function() {
        try {
          //var obj = (typeof XMLHttpRequest != "undefined") ? new XMLHttpRequest() : Engine.buildActiveX(Engine.ACTIVEX_XHR);
          if (typeof DummyHttpRequest != "undefined")
          {
              obj = new DummyHttpRequest;
          }
          else if (typeof SWFHttpRequest != "undefined")
          {
              obj = new SWFHttpRequest();
          }
          else if (typeof XMLHttpRequest != "undefined")
          {
              obj = new XMLHttpRequest();
          }
          else
          {
              obj = Engine.buildActiveX(Engine.ACTIVEX_XHR);
          }
          return obj;
        }
        catch(e) {
          Engine.reportException(null, e);
        }   
      };
      // ******************************

      and then on my index.html, I use this:

      // ******************************
      // from index.html
      // ******************************

      <html>
          <head>
              <script type="text/javascript" src="FlashHelper.js"></script>
              <script>
             
              var request_object = null;

              function getXMLNodeSerialisation(xmlNode) {
                  var text = false;
                  try {
                      // Gecko-based browsers, Safari, Opera.
                      var serializer = new XMLSerializer();
                      text = serializer.serializeToString(xmlNode);
                  }
                  catch (e) {
                      try {
                      // Internet Explorer.
                      text = xmlNode.xml;
                      }
                      catch (e) {}
                  }
                  return text;
              }

              // CODE FOR A VIRTUAL HTTPREQUEST WRAPPER
              function VirtualHttpRequest() {
                  this.url = '';
                  this.method = '';
                  this.protocol = 'http://';
                  this.contentType = 'text/xml; charset=UTF-8';
                  this.onreadystatechange = null;
                  this.readyState = null;
                  /*
                      0      Uninitated
                      1      Loading
                      2      Loaded
                      3      Interactive
                      4      Complete
                  */
                  this.responseText = null;
                  this.responseXML = null;
                  this.status = null;
                  this.statusText = null;
              }

              VirtualHttpRequest.prototype.open = function(method, address) {
                  this.url = address;
                  this.method = method;
                  /*
                  if(this.readyState == null)
                  {
                      this.readyState = 1;
                      this.addEventListener("load",
                                          function()
                                          {
                                              this.readyState = 4;
                                              if(typeof this.onreadystatechange == "function")
                                                  this.onreadystatechange();
                                          },
                                          false);
                  }
                  */
              }

              VirtualHttpRequest.prototype.send = function(req) {
                  this.readyState = 0;
                  var xmlString = getXMLNodeSerialisation(req);
                  //alert('******************************\nshoud send to '+this.protocol+this.url+' via '+this.method+' the following data:\n******************************\n'+xmlString);
                  doWsCall(this.protocol+this.url, this.method, xmlString, this.contentType, this);
              }

              VirtualHttpRequest.prototype.abort = function() {

              }

              VirtualHttpRequest.prototype.getAllResponseHeaders = function() {

              }

              VirtualHttpRequest.prototype.getResponseHeader = function(header) {

              }

              VirtualHttpRequest.prototype.setRequestHeader = function(header, value) {

              }

              function startApp(fs) {
                  this.status = 'started';
              }

              function getResponse() {
                  var response = FlashHelper.getFlash().GetVariable("retText");
                  request_object.responseText = response;
                  var xmlobject = (new DOMParser()).parseFromString(response, "text/xml");
                  request_object.responseXML = xmlobject;
                  request_object.readyState = 4;
                  request_object.status = 200;
                  request_object.onreadystatechange();
                  //alert(response);
              }

              function doWsCall(url, method, body, contentType, caller) {
                  request_object = caller;
                  request_object.readyState = 0;
                  var fs = FlashHelper.getFlash();
                  fs.XmlHttp(url, "getResponse", method, body, contentType);
              }

              </script>
              <script type="text/javascript" src="clean/source/clean-ajax-boot.js"></script>
              <script>
              function success(obj){
                // Apply result value on form
                //document.forms['calcutator'].result.value = obj;   
                  for (k in obj.result)
                      alert(k+':'+obj.result[k]);
              }

              function error(obj){
                // Display faultString    
                alert("Error: " + obj["faultString"]);
              }

              function showError(m) {
                  alert('got error:');
                  for (k in m)
                  {
                      alert(k+':'+m[k]);
                  }
              }

              function wscall() {
                method = 'CaptureFinger';
                n1 = 1;
                n2 = 2;
               // Create a message   
                var message = Clean.createSimpleMessage(
                "localhost:12333", "", showError);   
                // Create a remote method
                var rpc = new RemoteMethod();
                // Define the protocol
                rpc.protocol = "SOAP";
                // Define the web service method name
                rpc.name = method;
                // Insert params
                //rpc.addParam(n1);
                //rpc.addParam(n2);
                // Define event listeners
                rpc.onResult = success;
                rpc.onError = error;
                // Call web service remote method
                Clean.callWebService(message, rpc);           
              }
              </script>
          </head>
          <body>
              <input type='button' onclick='javascript:wscall()' value='wscall()'/>
              <script type="text/javascript">
              FlashHelper.height = 0;
              FlashHelper.width = 0;
              FlashHelper.writeFlash();
              </script>
          </body>
      </html>
      // ******************************

      So far so good, I am able to call my crossdomain webservice via your API ! This works great on firefox (tested on version 3 at least) but in Internet Explorer I get a strange error:
      Line 160 (of it doesnt tell which file)
      Char 3
      Error: 'HistoryTool' is undefined

      Do you think this could be a corrected bug for IE ? What can I do to go further ?

      Thank you for your patience,

      Patricio

       
    • Patricio Stegmann

      Sorry not DummyHttpRequest but VirtualHttpRequest

       
    • Carlos Eduardo Goncalves

      Hello Patricio,

      You are right, Flash is a great option to acomplish it. Your modifications are very intresting and can be very useful for other users too! Congratulations.

      Well lest see your problem.

      - Error: 'HistoryTool' is undefined

      I think that this error is caused by some problem on bootstrap, here we go:

      The script clean-ajax-boot.js declares the function prepareStart, which is used to detect when the page is full loaded. If some code try to use any class provided by Clean before the page is full loaded, messages like that can occur, because the classes are not attached to the page yet (we are using OnDemand JavaScript here).
      To solve it you can :

      - Try to declare clean-ajax-boot.js as your first script on page
      - Just for test comment any reference for HistoryTool (there are just few references to this class it will not be a painful task)
      - Try do use the single script version clean-ajax.all.js (the worst option for you because you'll have to edit it to apply your changes).

      Try it and tell me if the problem disappears.

      Best regards.

       
    • Patricio Stegmann

      Carlos,

      I will try to do what you suggested, however this happens 1 time of 10...

      I have created some libs to be able to cross-domain with Clean.
      If you are interested please let me know ! It is working great for me.

      I also added some code to allow execution of callbacks for other contexts than the Clean object/engine.

      /**
      * <p>Remote method constructor.</p>
      */
      function RemoteMethod() {
        /**
        * <p>Method name. Can be a <code>String</code> or a <code>QName</code>.</p>
        * @see QName
        */   
        RemoteMethod.prototype.name = null;   
        /** <p>Method parameters.</p> */     
        RemoteMethod.prototype.params = new Array();
        /** <p>Method protocol.</p> */     
        RemoteMethod.prototype.protocol = "SOAP";
        /** <p>Envent handler called to process the method result. Receives as parameter the parsed result object.</p> */     
        RemoteMethod.prototype.onResult = null;
        /** <p>Envent handler called if a fault occurs on remote method. Receives as parameter the parsed fault object.</p> */      
        RemoteMethod.prototype.onError = null;
        /** added by kpoman to allow call on different object context */
        RemoteMethod.prototype.context = null;
      };

      I have added the context porperty for the remotemethod prototype.
      This allow doing this for example:

                        if (this.remoteMethod.context != null) {
                          this.remoteMethod.onResult.call(this.remoteMethod.context, rpc_result);
                        }
                        else {
                          this.remoteMethod.onResult(rpc_result);
                        }

      which allows me to call a method from the object consuming the webservice etc... This is because using the 'this' object directly executes on the current object context so the onResult method would be copied to the Engine instead of being executed on the caller object.

      I have a small question about receiving data back from the soap webservice: I am receiving a structure which is a dictionnary containing some values, one of the is an array of values. the unserializing of it gives an Object (for the dic) and an Object for the dictionnary too ! I would expect it to give an Array !

      Thanks for any help.

       
  • Patricio Stegmann

    Hi again Cadu !
    As I was telling you, there is a bad behaviour on Internet Explorer in particular in versions newer than 6 (ie7 and ie8) using clean-ajax-boot.js … basically some items are not yet loaded when bootstrapping. I suppose they did some optimization on page loading times and it broke the bootstrapping of clean ajax.
    One solution could be to put all the code on the same script (-all.js). I dont know if there is a newer version of clean ajax that solves this issue !
    Thank you,

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.