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
[email protected]
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel