Thursday, 20 June 2013

Is ColdFusion's REST implementation more verbose than it needs to be?

G'day:
I've been using some REST web services at work recently (CFML calling .NET ones; I'm just working on the CFML side of things, unfortunately), and also pottering around with ColdFusion 10's REST stuff at home occasionally. And then I wrote my "things I ain't researching" article, and one of the entries in that was to look more at CF's REST implementation more thoroughly, as well as Taffy and Relaxation. That in turn reminded me of a thread on Twitter in which Sean was denigrating ColdFusion 10's approach to REST, instead espousing a by-convention approach to things, or just in general not having the REST config alongside the code itself. I don't necessarily agree with him on the latter (I don't necessarily disagree, either!), but it's thought-provoking.

One thing that persisted in my mind is that it seems like there's a lot of horsing about making a method REST-callable, and after discussing a conventions-based approach with both Sean and separately with Adam Tuttle, I turned my mind to that.

God my writing is awful today, sorry. Slightly hungover. Bear with me.

OK, so here's a CFC and a method:

component {

    public string function greet(required string name) {
        return "G'day #arguments.name#!";
    }
}

The minimum I seem to be able to get away with to "RESTify" this is:

component rest=true {

    remote string function greet(required string name restargsource="query") httpmethod="get" {
        return "G'day #arguments.name#!";
    }
}


What have I had to do:
  • Tell ColdFusion the CFC is REST-able (what's the term for that? "REST-ready"? "RESTful", I guess);
  • make the method remote;
  • identify how the argument value will be passed;
  • state the HTTP method the method will be called via.
On the face of it, none of that seems too egregious.

However what if I want to pass the argument value in the path, rather than the query string?

component rest=true {

    remote string function greet(required string name restargsource="path") httpmethod="get" restPath="greet/{name}" {
        return "G'day #arguments.name#!";
    }
}

Now I need to add a new parameter, to provide a pattern for how the argument will be passed in the path.

Obviously there's a lot more stuff one can do with ColdFusion's REST implementation, but I'm just focusing on the minimums here.

Looking at this code, I asked myself if this could not be streamlined slightly, but adopting some sensible defaults / conventions:
  • I need to specify a restargsource. Why? Why can't it just default to query by default? Or path, for that matter. It needs to be something - it's not an option for it to be "none" - and I think it's completely legit to pick a default and use it. Let's say query, as it requires the least additional bumpf for it to work. For other HTTP methods, different defaults could be employed, eg: "body" for POSTs.
  • I need to specify an httpmethod. Again, why? Why not just default to get if it's not specified? It's gonna be the most likely default value here.
  • I need to specify remote. I don't even think this is necessary. In a RESTful CFC, surely the default access would be remote? If it's anything else, sure, specify it, but I think it's safe to assume the default here will be remote.
  • If the restargsource is path, I need to specify a restpath. Why? By default, the convention could simply be "{argName}" for each argument in order, so if there's three arguments, the default restpath would be "{arg1}/{arg2}/{arg3}" where arg1, arg2, arg3 are the names of the arguments. This is a safe convention to follow, isn't it? Obviously one might wish to specify a different restpath, but one shouldn't have to, if one is happy to follow conventions.
  • One ought to be able to name methods for their intended HTTP methods, eg: if one has a get() method, then if one makes a GET request to the CFC, then get() will be called, etc. Obviously one should still be able to have other method names which have the httpmethod specified, but as a convention, one should simply be able to have methods named for the verbs they service.
Taking all this into consideration, we ought to be able to simply do this:

component rest=true {

    string function get(required string name)"{
        return "G'day #arguments.name#!";
    }
}

I think that ought to be enough to make that CFC be able to service GET requests.

What do you think?

--
Adam