HTTP GET And HTTP POST Are Sufficient (For Me) In ColdFusion
As of this writing, HTML supports two different HTTP methods: GET
and POST
. And for the last 25-years, I've been building web-based applications very successfully on top of these two operation. Many people, however, are not satisfied with these limitations; and, would like to see PUT
, PATCH
, and DELETE
added to the list of natively supported methods in HTML. Personally, I'm quite satisfied with GET
and POST
and haven't really experience any pain-points. So, I thought it might be interesting to try and articulate my perspective.
It's A Simpler Mental Model
Having two HTTP methods, GET
and POST
, creates a very simple mental model. You're either reading data with a GET
or you're mutating data with a POST
. There's no need to hand-wring about what type of mutation you're performing—are you creating new data, replacing data, or updating data—you're just mutating it. Period.
The fact that people even argue about when to use PUT
and when to use POST
is already a red flag (in my mind) that the addition of new HTTP methods adds confusion, not clarity.
It's All A Leaky Abstraction Anyway
The idea that you can have your HTTP method match your intent is already a fairly leaky abstraction. Even with GET
and POST
, the intent of the request only roughly matches what happens behind the scenes. When you GET
a resource, it may very well mutate the data that you're accessing. For example, when viewing a resource, the application may implicitly change the last_accessed_at
timestamp. And, when you make a request to "delete" a resource, the application may very well "soft delete" that resource by simply setting the deleted_at
timestamp.
In middle school science, we learned the concept of "significant digits". As I remember it, these significant digits mean that the outcome of a calculation can only be as accurate as the least accurate part of the equation. So, if one of your inputs is only accurate to two significant digits, your result can only be accurate to two significant digits (even if your calculator gives you an answer to the 8th decimal place).
With a web application, the "significant digits" are "read" and "mutate". You really have no idea what's going on behind the scenes. And, attempting to layer more semantics on top of these two actions is attempting to add clarity where clarity can't exist.
File-Based Routing "Just Works"
A lot of the discussion around HTTP methods tends to coincide with a discussion about Routers and resource definitions. But, many applications don't use sophisticated routers with pattern matching—they just have files that match the URL / resource being requested.
That's not to say that a router doesn't add value; only that we have to remember that the application landscape is extremely varied. And, that in a file-based routing situation, the ability to make a request with many different HTTP methods may not be meaningful or helpful.
If the point of having more HTTP methods supported natively in HTML is supposed to make things "easier", then starting any argument with, "In your router, you would..." seems like a non-starter. Meaning, it seems like what you're saying is that in order to make things easier you have to start by adding a lot of complexity.
GET
And POST
ColdFusion Effortlessly Differentiates Between In the ColdFusion world, a GET
request and a POST
request can be differentiated though the population of the url
and form
scopes, respectively. That's not to say that a POST
can't also supply url
parameters—only that ColdFusion has in-built mechanisms for differentiating URL-based data from body-based data.
This is particularly helpful for the highly prevalent "post back" pattern wherein a View rendered with a GET
can then POST
back to itself in order to perform an action. Think about something like a "delete confirmation" page wherein the application prompts the user to confirm the state mutation that is about to take place:
<cfscript>
param name="url.resourceID" type="numeric";
param name="form.submitted" type="boolean" default=false;
// Check for POST method by seeing if form scope is populated.
if ( form.submitted ) {
deleteResource( url.resourceID );
goto( "list.cfm" );
}
</cfscript>
<cfoutput>
<!--- POST back to self to perform mutation. --->
<form method="post" action="?resourceID=4">
<input type="hidden" name="submitted" value="true" />
<p>
Are you sure you want to delete this resource?
</p>
<button type="submit">
Yes, delete it
</button>
</form>
</cfoutput>
Here, we're able to effortlessly differentiate the initial GET
request from the subsequent POST
request by looking to see if form.submitted
was populated (with a truthy value).
What would this look like if we started trying to differentiate between more HTTP methods? Would we have to introduce new scopes? Would we have to start inspecting the cgi.request_method
in our processing logic? Would we have to drive all the data into the URL?
We could, of course, do that—we could add more mechanics to the language or add more logic to the processing. But, I only argue that this wouldn't really be solving any problems—it would only be swapping one check for another (on top of what is already a very leaky abstraction, see above).
DELETE
Can't Have A Body
Note: From what I'm seeing on Google, this has more to do with server implementations than it does with the actual HTTP specification.
This is 100% subjective; but, to me it feels very strange that a DELETE
request cannot have a body. Which is to say that all data conveyed in a DELETE
request must be present in the URL (or in the HTTP headers). This is probably just me fighting against the gravity of 25 years of web development; but, all mutation data feel like it should be at—least partially—outside of the URL.
I'll concede that this discomfort is likely me just trying to reconcile the old world with the new world. But, let's just look at the previous example using a theoretically-supported DELETE
verb in HTML:
<cfscript>
param name="url.resourceID" type="numeric";
param name="url.submitted" type="boolean" default=false;
// Check for DELETE method by seeing if url scope is populated.
if ( url.submitted ) {
deleteResource( url.resourceID );
goto( "list.cfm" );
}
</cfscript>
<cfoutput>
<!--- DELETE back to self to perform mutation. --->
<form method="delete" action="?resourceID=4&submitted=true">
<p>
Are you sure you want to delete this resource?
</p>
<button type="submit">
Yes, delete it
</button>
</form>
</cfoutput>
Again, this is 100% subjective and mostly emotional. But it just feels "wrong" to examine nothing but the url
scope in order to perform a delete operation.
That said, imagine a delete confirmation form in which I had to upload a file as part of the deletion process? Maybe I have a PDF that documents permission for me to perform the delete. How would I even do that in a "semantic" way? I don't think DELETE
can coincide with an upload (on most server implementations). I think (and maybe I'm just naive here) that you'd have to use a POST
in order to perform a deletion with a file upload.
Which again, if true, just points to the "theater" of it all (that is, pretending like the semantics of a DELETE
are somehow more meaningful than the semantics of a POST
).
Bulk Operations Aren't Confusing
This goes back to my earlier point that GET
and POST
create a simple mental model. What happens if you want to delete multiple resources at a time? In a GET
and POST
world, the answer is obvious: you POST
to the server with the collection of resource identifiers that you want to delete.
But, in a world where DELETE
is supported, should this be a DELETE
operation? You're not talking about a single resource - you're talking about many resources. Does a DELETE
make sense in that case? I don't know. And, in a GET
/POST
world, I don't have to care.
PUT
, PATCH
, And DELETE
It's OK If You Want To Use To be clear, I'm not here to yuck your yum. As someone who loves ColdFusion, I can totally relate to the notion that other people can't see the magic in the way you want to work. And, if you want HTML to support these methods so that you can use them in your applications, I think that's fine. My only point in this post is that these HTTP method don't inherently make life easier (or more intentful) for my ColdFusion applications.
Want to use code from this post? Check out the license.
Reader Comments
Well said! I think your strongest point was on bulk DELETE.
Another reason could just be simply the inconsistent application of the verbs.
@Chris,
Well you're a CFWheels guy, if I recall from a previous conversation. So, I know (or at least I'm learning now) that Wheels does a lot of this behind the scenes for you (with the
_method
hidden fields) as long as you pass the right route to thestartFormTag()
.That said, the biggest workflow difference for me in the Wheels world is that it doesn't use the "post back" kind of workflow that I have always done. There's a lot of
POST
ing to a different controller (ie,new
posts over tocreate
, which then re-renders thenew
View as needed). This is taking me a bit to get used to; especially with the two different controller methods having to prepare the same data for a view.But, there's cool stuff here - lots of magic to learn.
@Ben Nadel,
Indeed I am. Filters are your friend when multiple routes all work with the same model to help cut down on the boilerplate. But I'm sure you know that already.
It does require a bit of a mental shift, but I like how it also keeps things orderly and consistent as well.
I'm very slowly learning - it's like drinking from a fire hose right now 🙃
You didn't mention REST, but some of my criticisms of that pattern align with your frustration. While I'm not convinced that GET and POST are the holiest of HTTP verbs, I believe HTTP (and subsequently REST) do not have good answers for bulk operations. In your basic example, I would, and have, just provided a list in the query string.
@Jason,
100% I'm not saying that
GET
andPOST
are perfect - only that they are simple and sufficient. The older I get, the more I like simple.Hey Ben,
Yep, HTML only support GET and POST, and probably always will, and I do understand where you are coming from.
I also love your point about keeping things simple, I am a huge proponent and wish others would do the same.
So yeah, you could write any app with only those two methods. No issue there.
And if it speeds your productivity of building great CF apps, it really doesn't matter what anyone thinks.
Keep this in mind however... it's important to know who your intended users are.
If they are people clicking on a browser, no problem.
But if they are servers run by third-party engineers connecting to your API, then consider following standard REST principles and semantics instead.
Cheers
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →