Pseudo-RESTful
I have to admit it: I spent the last week rolling my own data store that handles lazy loading that I thought was going to be the greatest thing since… well anyway. Then I read this , and realized that there was no need to write almost any of what I had written. I hate when that happens.
In any case, the new JsonRestStore does just about everything I wanted to do, only more efficiently. My only problem is that my server, which I have no control over, has blocked “PUT” and “DELETE”. But really, that seems minor. I mean, couldn’t we just include a flag to our REST server telling it what action we are expecting? Yes. We can.
A detailed look into dojox.rpc.Rest reveals that the _change method is where the final call to xhr occurs. Now I know this is unsafe since this is a private method, but its a good quick fix for now. And perhaps I can convince the dojo folks after 1.2 is official that they should support this kind of behavior for “Pseudo-REST” services, right out of the box.
Here is the code.
/** * * uses post for all post/put/delete, but adds and attribute called action * * @author maulin * */ dojo.provide("medryx.stores.ModifiedRest"); dojo.require("dojox.rpc.Rest"); (function() { //copied verbatim from dojox.rpc.Rest -- it is local in scope inside Rest //and called from the _change method, so I need it (for now) function index(deferred, service, range, id){ deferred.addCallback(function(result){ if(id==""){ service._rootQueries.push(result); } if(range){ // try to record the total number of items from the range header range = deferred.ioArgs.xhr.getResponseHeader("Content-Range"); deferred.fullLength = range && (range=range.match(/\/(.*)/|>)) && parseInt(range[1]); } return service.cache.intake(result,id); }); return deferred; } //"over-rides" the actual xhr call to use POST and add action attribute to querystring dojox.rpc.Rest._change = function(method,service,id,content){ // this is called to actually do the put, post, and delete var request = service._getRequest(id); request[method+"Data"] = content; //ADDED THE FOLLOWING TWO LINES request.data += "&action=" + method.toLowerCase(); request.url += (request.url.indexOf('?') > 0 ? "&" : "?" ) + "action=" + method.toLowerCase(); //CHANGED THIS LINE TO ALWAYS USE POST return index(dojo.xhr("POST",request,true),service); }; })();
May I suggest something like this for REST blocking servers:
var defaultXhr = dojo.xhr;
dojo.xhr = function(method,args,hasBody){
if(method!=”GET”){
args.url += (args.url.indexOf(’?') > 0 ? “&” : “?” ) + “action=” + method.toLowerCase();
if(method==”PUT”){
args.postData = args.putData;
}
method = “POST”;
}
return defaultXhr(method,args,hasBody);
}
That way all your requests that use PUT, POST, and DELETE will automatically have the correct “action” parameter. I didn’t test this, but I think it should work.
Comment by Kris Zyp — June 18, 2008 @ 5:56 pm
great idea. thanks.
I actually thought about this “hack” some more, and had a more general thought: The reason I like JsonRestStore is that it has a VERY nice entity-manager (for lack of a better term). Its not that I need or want REST (I have a simple working RPC server already working with get/save/delete methods).
I think the JsonRestStore actually has mixed concerns — on the one hand an entity-manager, and on the other a “persister”. Perhaps we could think about how to split the two, and then make REST a pluggable persister into the entity-manager?
Comment by maulin — June 18, 2008 @ 7:29 pm
Maulin,
I am not exactly certain what you mean by entity-manager, are you referring to the mechanism for triggering lazy loads on property access and recording property changes? I am not sure how you would like JsonRestStore split up. Here is the current breakdown of modules:
JsonRestStore - Dojo Data API adapter
JsonRest - Tracks changes and fires Rest services
Rest - Remote communication service provider
dojox.json.ref - Referencing manager for lazy loading and other forms of referencing
Perhaps JsonRest or JsonRestStore would roughly correspond to your “entity-manager”, and the Rest service would correspond to your “persister”, not sure. You can definitely plug your own custom Rest service into JsonRestStore. It looks like:
myService = function(id){ … get data … }
myService.put = function(content) { … save data …}
myService.post = function(target,content) { .. create new object …}
myService['delete'] = function(target){ … delete object …}
If you providing getters and setters is what you have in mind for an entity-manager, you can also define schemas for JsonRestStore that define the prototype to use for the objects in the store:
mySchema = {
prototype: {
getFoo:function(){return this.foo},
setFoo:function(val){this.foo =val}
}
}
myStore = new JsonRestStore({service:myService,schema:mySchema});
Comment by Kris Zyp — June 19, 2008 @ 6:28 am
Ugh. Yes. That is EXACTLY what I meant. I guess I didn’t understand this as thoroughly as I had hoped. I have created a slightly different approach to this, but now seeing how JsonRestStore is setup, I don’t know how much value add there is. We’ll see. I plan on posting about it later tonight.
thanks again for your input.
-maulin
Comment by maulin — June 19, 2008 @ 6:35 am