Derrell Lipman wrote > Got it. The ExtJs server apparently implements something that claims to be > JSON-RPC, but doesn't follow the JSON-RPC protocol. (The field "id" is not > allowed to be called "tid" in a JSON-RPC request or response.) > > So, you have a couple of choices. The easy one for me to suggest (since > I'm > not implementing your server) is for your server to copy the tid field in > the response to a new field called id. That way, the response will work > for > both ExtJs clients and qooxdoo ones. > > If that's not a feasible solution, you can try something like this: > > - extend qx.io.remote.Rpc > - override the method createRequest as follows (untested) > > createRequest : function() > { > var req = this.base(arguments); // get the request object > > // Add a listener to munge the response. Since this listener > // is added before the one in _callInternal, this one should be > // called before the one in _callInternal. > req.addListener( > "completed", > function(evt) > { > // get a reference to the response object > var response = evt.getContent(); > > // If we got a response... > if (response !== null) > { > // Modify the response object in the event content. > // You may want a conditional here: only overwrite if id > // member does not already exist. > response["id"] = response["tid"]; > } > }); > > // return the request object > return req; > }
You're completely right about the 'id' requirement. Unfortunately, there is no easy way to fix this in the qooxdoo framework. I tried to follow you're suggestion, only the response event doesn't contain a response object, but a response json string that has to be parsed. The parsing happens in the code below, which is (just a single function), too my opinion far to long and and impossible to extend (not quite the open closed principle). So, I think I'll have to start looking at fixing this in on the server side. This is the function that parses the response object and checks the id prop: _callInternal : function(args, callType, refreshSession) { var self = this; var offset = (callType == 0 ? 0 : 1); var whichMethod = (refreshSession ? "refreshSession" : args[offset]); var handler = args[0]; var argsArray = []; var eventTarget = this; var protocol = this.getProtocol(); for (var i=offset+1; i<args.length; ++i) { argsArray.push(args[i]); } var req = this.createRequest(); // Get any additional out-of-band data to be sent to the server var serverData = this.getServerData(); // Create the request object var rpcData = this.createRpcData(req.getSequenceNumber(), whichMethod, argsArray, serverData); req.setCrossDomain(this.getCrossDomain()); if (this.getUsername()) { req.setUseBasicHttpAuth(this.getUseBasicHttpAuth()); req.setUsername(this.getUsername()); req.setPassword(this.getPassword()); } req.setTimeout(this.getTimeout()); var ex = null; var id = null; var result = null; var response = null; var handleRequestFinished = function(eventType, eventTarget) { switch(callType) { case 0: // sync break; case 1: // async with handler function handler(result, ex, id); break; case 2: // async with event listeners // Dispatch the event to our listeners. if (!ex) { eventTarget.fireDataEvent(eventType, response); } else { // Add the id to the exception ex.id = id; if (args[0]) // coalesce { // They requested that we coalesce all failure types to // "failed" eventTarget.fireDataEvent("failed", ex); } else { // No coalese so use original event type eventTarget.fireDataEvent(eventType, ex); } } } }; var addToStringToObject = function(obj) { if (protocol == "qx1") { obj.toString = function() { switch(obj.origin) { case qx.io.remote.Rpc.origin.server: return "Server error " + obj.code + ": " + obj.message; case qx.io.remote.Rpc.origin.application: return "Application error " + obj.code + ": " + obj.message; case qx.io.remote.Rpc.origin.transport: return "Transport error " + obj.code + ": " + obj.message; case qx.io.remote.Rpc.origin.local: return "Local error " + obj.code + ": " + obj.message; default: return ("UNEXPECTED origin " + obj.origin + " error " + obj.code + ": " + obj.message); } }; } else // protocol == "2.0" { obj.toString = function() { var ret; ret = "Error " + obj.code + ": " + obj.message; if (obj.data) { ret += " (" + obj.data + ")"; } return ret; }; } }; var makeException = function(origin, code, message) { var ex = new Object(); if (protocol == "qx1") { ex.origin = origin; } ex.code = code; ex.message = message; addToStringToObject(ex); return ex; }; req.addListener("failed", function(evt) { var code = evt.getStatusCode(); ex = makeException(qx.io.remote.Rpc.origin.transport, code, qx.io.remote.Exchange.statusCodeToString(code)); id = this.getSequenceNumber(); handleRequestFinished("failed", eventTarget); }); req.addListener("timeout", function(evt) { this.debug("TIMEOUT OCCURRED"); ex = makeException(qx.io.remote.Rpc.origin.local, qx.io.remote.Rpc.localError.timeout, "Local time-out expired for "+ whichMethod); id = this.getSequenceNumber(); handleRequestFinished("timeout", eventTarget); }); req.addListener("aborted", function(evt) { ex = makeException(qx.io.remote.Rpc.origin.local, qx.io.remote.Rpc.localError.abort, "Aborted " + whichMethod); id = this.getSequenceNumber(); handleRequestFinished("aborted", eventTarget); }); req.addListener("completed", function(evt) { response = evt.getContent(); // server may have reset, giving us no data on our requests if (response === null) { ex = makeException(qx.io.remote.Rpc.origin.local, qx.io.remote.Rpc.localError.nodata, "No data in response to " + whichMethod); id = this.getSequenceNumber(); handleRequestFinished("failed", eventTarget); return; } // Parse. Skip when response is already an object // because the script transport was used. if (!qx.lang.Type.isObject(response)) { // Handle converted dates if (self._isConvertDates()) { // Parse as JSON and revive date literals if (self._isResponseJson()) { response = qx.lang.Json.parse(response, function(key, value) { if (value && typeof value === "string") { if (value.indexOf("new Date(Date.UTC(") >= 0) { var m = value.match(/new Date\(Date.UTC\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)\)/); return new Date(Date.UTC(m[1],m[2],m[3],m[4],m[5],m[6],m[7])); } } return value; }); // Eval } else { response = response && response.length > 0 ? eval('(' + response + ')') : null; } // No special date handling required, JSON assumed } else { response = qx.lang.Json.parse(response); } } id = response["id"]; if (id != this.getSequenceNumber()) { this.warn("Received id (" + id + ") does not match requested id " + "(" + this.getSequenceNumber() + ")!"); } // Determine if an error was returned. Assume no error, initially. var eventType = "completed"; var exTest = response["error"]; if (exTest != null) { // There was an error result = null; addToStringToObject(exTest); ex = exTest; // Change the event type eventType = "failed"; } else { result = response["result"]; if (refreshSession) { result = eval("(" + result + ")"); var newSuffix = qx.core.ServerSettings.serverPathSuffix; if (self.__currentServerSuffix != newSuffix) { self.__previousServerSuffix = self.__currentServerSuffix; self.__currentServerSuffix = newSuffix; } self.setUrl(self.fixUrl(self.getUrl())); } } handleRequestFinished(eventType, eventTarget); }); // Provide a replacer when convert dates is enabled var replacer = null; if (this._isConvertDates()) { replacer = function(key, value) { // The value passed in is of type string, because the Date's // toJson gets applied before. Get value from containing object. value = this[key]; if (qx.lang.Type.isDate(value)) { var dateParams = value.getUTCFullYear() + "," + value.getUTCMonth() + "," + value.getUTCDate() + "," + value.getUTCHours() + "," + value.getUTCMinutes() + "," + value.getUTCSeconds() + "," + value.getUTCMilliseconds(); return "new Date(Date.UTC(" + dateParams + "))"; } return value; }; } req.setData(qx.lang.Json.stringify(rpcData, replacer)); req.setAsynchronous(callType > 0); if (req.getCrossDomain()) { // Our choice here has no effect anyway. This is purely informational. req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); } else { // When not cross-domain, set type to text/json req.setRequestHeader("Content-Type", "application/json"); } // Do not parse as JSON. Later done conditionally. req.setParseJson(false); req.send(); if (callType == 0) { if (ex != null) { var error = new Error(ex.toString()); error.rpcdetails = ex; throw error; } return result; } else { return req; } }, -- View this message in context: http://qooxdoo.678.n2.nabble.com/qx-io-remote-Request-51-1-Received-id-undefined-does-not-match-requested-id-tp7586683p7586716.html Sent from the qooxdoo mailing list archive at Nabble.com. ------------------------------------------------------------------------------ New Year. New Location. New Benefits. New Data Center in Ashburn, VA. GigeNET is offering a free month of service with a new server in Ashburn. Choose from 2 high performing configs, both with 100TB of bandwidth. Higher redundancy.Lower latency.Increased capacity.Completely compliant. http://p.sf.net/sfu/gigenet _______________________________________________ qooxdoo-devel mailing list qooxdoo-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel