From the comments below, several very interesting resources have surfaced. The most damning of these articles is an nine-year-old bog entry by Roy Fielding (father of REST). This quote appears:
What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint? In other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period. Is there some broken manual somewhere that needs to be fixed?
So, categorically, unless we are using HTTP to directly manage state - and not merely as a messaging layer - we are not RESTful.
Also thanks to all those with constructive comments that helped square away my understanding of a RESTful architecture. While I don't care for the coupling that comes from intertwining the app with the protocol used to communicate between client and server, its clear that decoupling those items _violates the intended definition of REST.
Where to go from here?
Not sure yet. I owe it to myself to take a look at the "HATEOAS (Hypertext As The Engine Of Application State)" mentioned in the Fowler article on RMM. I need to reconcile the dual role of HTTP to provide both transport and management of application state.
note bene: I'm really looking forward to comments that begin, "What about...," or "In addition, ..." as its a great benefit to enumerate additional considerations. Thanks
We develop and operate a private web portal for clients. That portal allows clients to login, save information, license intellectual property, and generate highly customized, print-ready PDFs for use in their business. As such, the API is not open and we do not integrate with other apps or services. The lack of such integration grossly simplifies what we need to do as it eliminates many of the normal benefits received by following the current convention. That said, ...
A faithful RESTful implementation necessitates use of appropriate HTTP verbs.
this motivation is no longer a concern. However, I'm not finished chewing on the bone I found.
The reason for pursuing this line of thought at all was out of concern for reducing the amount of information exposed in server log files. So, it began
more as a curiosity than as a possible solution to a specific problem. To that end, this reads better as thinking out loud rather than a manifesto for revolution.
If I'm using the usual and customary RESTful implementation, the path (but naturally not the non-GET payloads) appears in the server logs. A reasonable application of knowledge and an exposure in authored or incorporated software could lead to a breach. While I like the idea of being able to scrape server logs for intelligence, in practice I just don't do it. There are other, more intelligent means by which to gain that kind of insight regarding usage. Therefore, information is being exposed without offering any value in return for the perceived risk.
The client-side software and the server-side software don't require the use of HTTP Verbs to communicate intent -- those two pieces can communicate with each other, provided the browser and web server can communicate.
ergo, we require only that the communications are successful; and ask the browser and web server (Apache, iis, nginx, etc) to provide the comm link or information about why the comm link is unavailable. That's their job, let them do it. But don't ask them to take on responsibility for the entire solution.
The alternative: encapsulate solution-specific server-side response codes and data within the payload.
To that end, we elect to simply prefer the POST method with a message header and message body to communicate intent currently conveyed via HTTP verbs. We accept use of the GET verb where we don't mind exposing our RESTful structure (...or wish to permit bookmarks or support caching).
The message header contains two field (hidden inputs, if you will):
- request_type : New | Create | Edit | Update | Show | List | Delete
- request_path : /resource/id/resource2/id2/...
The message body contains all the usual suspects.
*---------------* | request_type | | request_path | +---------------+ | field1 | | field2 | | ... | | fieldN | *---------------*
Solution-specific, server-side responses are provided with a response header and response body. The response header takes on the responsibility for conveying the status of the request (replacing use of HTTP status codes formerly used for this purpose) while the response body contains any requested data as per usual.
HTTP headers, ... HTTP_STATUS: 200, 400, 404 data: *------------------* | response_status | +------------------+ | field1 | | field2 | | ... | | fieldN | *------------------*
I think this decouples the various browsers and web servers from my client code and server-side code. I still have to use POST and GET, but I only have to use POST and GET. And GET could be optional and rare, since I still want to eliminate information from server logs.
- Caching will be affected by using POST where GET is a legitimate candidate; to be investigated
- Bookmarking will be affected by using POST where GET is a legitimate candidate; to be investigated
- Currently accepted principles will be violated by using POST where GET is a legitimate candidate; tradeoffs will be evaluated
(Still too long; but I'm curious)
Here's a thought process that was radial and branching in nature, but had to be tortured into a linear space, here.
I've spent a few years torturing our web app from a hacked solution towards a more RESTful solution. I love the simplicity of the relative path address path and the nature of the seven operations - eight for us, since I've got a parking lot item to handle search as a first-class request.
But during that time, I've struggled with the implementation of HTTP verbs and REST. Put simply, HTML doesn't support use of all VERBs. Form methods may only be GET or POST and one can fairly ask why support GET. However, the PUT, PATCH, and DELETE options are not supported.
Barring some unknown consideration, it makes sense to me that the HTML form spec allow for:
- method=POST to create a resource
- method=PATCH to update a resource
- method=PUT to replace a resource
- method=DELETE to delete resource
So what good are you? we jumped through some hoops only to have my intentions squashed on the server. No thanks.
Found this resource which nicely captures the use of HTTP status codes to response to HTTP verbs. However, the vast majority have more to do with app response than web server communications response to the browser:
|HTTP verb||status code||App Scope||Web Server Scope|
|POST (create)||201 created||Yes||-|
|POST||404 not found||Maybe||Maybe|
|GET (read)||200 (OK)||Yes||-|
|PUT||204 (no content)||Yes||-|
|PUT||405 (method not allowed)||Yes||-|
|PATCH||204 (no content)||Yes||-|
|PATCH||405 (method not allowed)||Yes||-|
|DELETE||405 (method not allowed)||Yes||-|
Instead of this mess, I'd rather just focus my communications between browser and web server on the 200 (OK), 400 (bad request), and 404 (not found) responses. Either communications was good and I ask the app to continue processing using the data returned, or the end point was not found (404) and we handle that response as appropriate.
The HTTP status codes can provide some guidance for app return codes in that we may want a standardized response where
|Stolen HTTP Status Code||means what it means|
|201||success on creation of resource|
|204||there is no content to be displayed|
|404||bad resource id|
|405||method not allowed|
|409||resource already exists (duplicate?)|
|418||in the event we ask a teapot to brew coffee|
But, these are returned in an app response header, not the HTTP response.
This comes down to the incurred cost of developing or using a package to support AJAX communications that are formally correct but get tortured into a POST by the time the web server passes the request to server-side code. It may only become a personal preference to branch off a request header rather than a verb in the communications overhead. Either way, the detection work still needs to be done. But I feel better working off the request than the communications header.
...continuing, pulling my action detection out of the comms header and into a request header allows for separation of web server code from request code. That is, my server script end point merely has to pull the request header and body from the web server and feed it to the server-side code. Now, we could interpret the web server request and translate it for the server-side code, by why introduce that action at all ? Why not decouple altogether and simply state the action in a request header which passes untouched from the client-side to the server-side ?
Revisiting the whole use of HTTP status codes to tell me about something that happened in the server-side code (beyond the web server). Just as I dont want the server-side code using information from the comm header (GET|POST) to know what action to take (NEW|CREATE|EDIT|UPDATE|SHOW|LIST|DELETE), I don't want the client-side code to take app-related action based on information in the comm response.
Why should the comm response be anything (for a web app) beyond 200, 400, 404 ?
Now, in the event, the API is to be exposed for third-party usage that (historically) expects app-related responses from the comm layer, I get it. The legacy reasons for including those responses may be overwhelming.
I also expect this will have mocking/testing benefits although a proper decoupling of server side code may have accomplished this anyway, yielding no incremental benefit.
Standardizing the request header and request body on both the server and client sides means only dealing with a single model for creating and responding to a request. Check.
Reducing the ajax communications to concerns related to GET, POST and the pass/fail nature of the communications link streamlines communications processing between client and server. Check.
Implementing a response body into the data portion of a response complicates the client-side processing. A bit. Ugh.
Oh, and I don't expose any of our structure in server logs. When the path goes into the payload instead of the URI, the structure is hidden. That may not have been a concern a decade ago, but I am beginning to think it should be more of a concern today. Just as we played fast and loose with all of early IoT toys around the house, we may be flirting with danger allowing our resource end points to appear in server logs.
Thanks for taking the time to read this. And even more thanks if you choose to respond/comment with constructive criticism. Kudos.