Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re^3: modern ways of doing web services - avoid REST too

by Rhandom (Curate)
on Sep 27, 2013 at 15:32 UTC ( [id://1056003]=note: print w/replies, xml ) Need Help??


in reply to Re^2: modern ways of doing web services
in thread modern ways of doing web services

I'm sorry in return for even hinting about using something other than JSON.

REST is still wrong (even though it is better than SOAP). I actually feel strongly enough about it that I will explain in length why for posterity's sake.

My post began with "use JSON POSTing over HTTPS. Always." Mistakenly I added an update to use "whatever" which you seem to have grasped onto. I stand by the first part: "use JSON POSTing over HTTPS. Always."

Here are my reasons why to consistently use JSON POSTing over HTTPS always rather than use REST:

You yourself mention that your "fairly half baked" REST services is "not particularly RESTy." This seems to happen often when people claim to follow REST. I have no doubt whatsoever that your solution you mentioned was absolutely brilliant (and I'm very glad you used REST instead of SOAP). But if it wasn't truly REST, or if even if it was, the client consumers likely had to write custom code.

If you do have a REST client in any language on any platform - it will work with my "JSON POSTing over HTTPS" because JSON POSTing over HTTPS RESTish (1/4th of REST).

If you don't have a REST client, it will take fewer lines of code and will be far more consistent to make your client speak to a consistent "JSON POSTing over HTTPS" than it will be to communicate with any REST based service.

If you are writing a web based service, and are using *any* library to do your REST work (such as Mojo or the other frameworks, or some custom service), you can use that very same service to do consistent JSON POSTing over HTTPS for every method.

If you intend to consume REST from the browser, you need a plugin to manage it. If you "always use JSON POSTing over HTTPS", it is easy to make beautiful unified API pages that make requests to the service (ajax consumption works just fine, but our consistent server layer also allows for normal x-www-urlencoded browser requests in addition to JSON POST for development and discovery purposes but we leave the normal client communication as JSON POSTing over HTTPS).

I have consumed many REST services and each time I have these thoughts: PUT and DELETE are contrived; but at least this isn't SOAP.

I don't like to use numbers to back up arguments, but we have at least 9 service APIs pulling over 12 million hits a day spiking to 20 million on some days (I know this is small compared to some shops). And they have consistently been doing this for over 7 years. Every single service is JSON POSTing over HTTPS. It is so consistent that our consuming code looks like this (seriously):
my $client = API::Client->new({service => "dist"}); my $data = $client->server_info({hostname => "foo.bar.com"}); # this maps out to https://someserver.com/dist/server_info POSTing +{"hostname":"foo.bar.com"}
It is so consistent that our server code looks like this (our methods are self documenting and discoverable):
package DistService; use API::Base; sub server_info__meta { return { desc => 'Returns information about a server', args => { server_id => {desc => 'The Id of this server', required => + 1}, }, resp => {success => 1}, }; } sub server_info { my ($self, $args) = @_; my %data; ... return \%data; }
The API::Client code ends up being simple wrappers around configuration and existing perl network libraries. And we have consumers around the world running on many varied platforms. And they all had an easy time consuming because we "always use JSON POSTing over HTTPS." Just like REST (but moreso) it is even trivial to use curl or wget if you use consistent JSON POSTing over HTTPS.

If you have commandline clients (every single one of our clients and servers has commandline interfaces) keeping the nouns and verbs in your consistent method names allows for calling the exact same method names on the commandline as you do in your API and even can keep them the same as listed in your code. You have to come up with new and creative ways of representing method names if you want to use REST since the noun and verb are separate. Here is a sample:

[rhandom@somehost ~/%] ds server_info hostname foo.bar.com Arguments: +----------+-------------+ | hostname | foo.bar.com | +----------+-------------+ Data: +---------------+---------------------+ | access_tag | MDEV | | added_by_uid | 33 | | date_added | 2013-09-27 09:17:47 | | description | - | | hostname | foo.bar.com | | ip | 0.0.6.11 | | server_id | 26208 | +---------------+---------------------+
Notice the client code, and the url, and the server code, and the commandline code all use the exact same method name - consistency will save time and frustration. You cannot do that with REST.

Our implementation of JSON POSTing over HTTPS is consistent enough and simple enough and clean enough that we will likely eventually release our API based code to CPAN, despite the plethora of existing RPC tools on CPAN already.

REST clients require you to determine which parts (if any) are the resource identifiers used in the URL, and which parameters are part of the JSON request, and then you have to keep your noun and verb separate. Consistent JSON POSTing over HTTPS allows for simple method/arg calls without having to do any parameter mapping.

REST PUT and DELETE responses do not return data (or shouldn't if you are following real REST). Typically it is *very* useful to actually get back information from a delete (such as rows deleted, or if the record was already gone, or if it is pending, etc). Consistent JSON POSTing over HTTPS allows for returning data with any type of action.

Lets look at the apache logs. Here is a tiny sampling from one of our lower hit services (with the ips munged to protect the innocent):
0.0.20.86 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/server_info HTT +P/1.0" 200 292 "-" "-" 0.0.66.29 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/unmanaged_add H +TTP/1.0" 200 21 "-" "-" 0.0.80.14 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/undone_list HTT +P/1.0" 200 218214 "-" "-" 0.0.69.86 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/unmanaged_add H +TTP/1.0" 200 21 "-" "-" 0.0.54.28 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/unmanaged_add H +TTP/1.0" 200 21 "-" "-" 0.0.80.16 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/undone_list HTT +P/1.0" 200 302337 "-" "-" 0.0.48.23 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged_add H +TTP/1.0" 200 21 "-" "-" 0.0.19.71 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/server_info HTT +P/1.0" 200 290 "-" "-" 0.0.80.17 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/undone_list HTT +P/1.0" 200 146432 "-" "-" 0.0.50.12 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/server_info HTT +P/1.0" 200 294 "-" "-" 0.0.27.29 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/server_info HTT +P/1.0" 200 293 "-" "-" 0.0.19.22 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/server_info_fil +es HTTP/1.0" 200 30321 "-" "-" 0.0.58.10 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged_add H +TTP/1.0" 200 21 "-" "-" 0.0.19.22 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged_add H +TTP/1.0" 200 21 "-" "-" 0.0.63.83 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged_add H +TTP/1.0" 200 21 "-" "-" 0.0.86.42 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/server_info HTT +P/1.0" 200 273 "-" "-" 0.0.24.47 - - [27/Sep/2013:08:12:52 -0600] "POST /dist/server_info HTT +P/1.0" 200 292 "-" "-" 0.0.44.15 - - [27/Sep/2013:08:12:52 -0600] "POST /dist/server_info HTT +P/1.0" 200 294 "-" "-" 0.0.33.11 - - [27/Sep/2013:08:12:52 -0600] "POST /dist/server_info HTT +P/1.0" 200 293 "-" "-" 0.0.15.80 - - [27/Sep/2013:08:12:52 -0600] "POST /dist/server_info HTT +P/1.0" 200 290 "-" "-"
Here is what the equivalent looks like if it was REST:
0.0.20.86 - - [27/Sep/2013:08:12:50 -0600] "GET /dist/server/32 HTTP/1 +.0" 200 292 "-" "-" 0.0.66.29 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/unmanaged/33 HT +TP/1.0" 200 21 "-" "-" 0.0.80.14 - - [27/Sep/2013:08:12:50 -0600] "GET /dist/undone HTTP/1.0" + 200 218214 "-" "-" 0.0.69.86 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/unmanaged/32 HT +TP/1.0" 200 21 "-" "-" 0.0.54.28 - - [27/Sep/2013:08:12:50 -0600] "POST /dist/unmanaged/32 HT +TP/1.0" 200 21 "-" "-" 0.0.80.16 - - [27/Sep/2013:08:12:50 -0600] "GET /dist/undone HTTP/1.0" + 200 302337 "-" "-" 0.0.48.23 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged/23 HT +TP/1.0" 200 21 "-" "-" 0.0.19.71 - - [27/Sep/2013:08:12:51 -0600] "GET /dist/server/36 HTTP/1 +.0" 200 290 "-" "-" 0.0.80.17 - - [27/Sep/2013:08:12:50 -0600] "GET /dist/undone HTTP/1.0" + 200 146432 "-" "-" 0.0.50.12 - - [27/Sep/2013:08:12:51 -0600] "GET /dist/server/31 HTTP/1 +.0" 200 294 "-" "-" 0.0.27.29 - - [27/Sep/2013:08:12:51 -0600] "GET /dist/server/38 HTTP/1 +.0" 200 293 "-" "-" 0.0.19.22 - - [27/Sep/2013:08:12:51 -0600] "GET /dist/server/32_files +HTTP/1.0" 200 30321 "-" "-" 0.0.58.10 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged/23 HT +TP/1.0" 200 21 "-" "-" 0.0.19.22 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged/25 HT +TP/1.0" 200 21 "-" "-" 0.0.63.83 - - [27/Sep/2013:08:12:51 -0600] "POST /dist/unmanaged/26 HT +TP/1.0" 200 21 "-" "-" 0.0.86.42 - - [27/Sep/2013:08:12:51 -0600] "GET /dist/server/38 HTTP/1 +.0" 200 273 "-" "-" 0.0.24.47 - - [27/Sep/2013:08:12:52 -0600] "GET /dist/server/33 HTTP/1 +.0" 200 292 "-" "-" 0.0.44.15 - - [27/Sep/2013:08:12:52 -0600] "GET /dist/server/34 HTTP/1 +.0" 200 294 "-" "-" 0.0.33.11 - - [27/Sep/2013:08:12:52 -0600] "GET /dist/server/38 HTTP/1 +.0" 200 293 "-" "-" 0.0.15.80 - - [27/Sep/2013:08:12:52 -0600] "GET /dist/server/33 HTTP/1 +.0" 200 290 "-" "-"
REST *can* or *might* win here if you care about seeing the resource id (which I have greatly simplified in this REST example). However if I'm using most log analyzation tools or even live log viewing tools including toys like logstalgia, the consistent POST over HTTPS wins because the URL includes the noun AND the verb.

REST cares too much about your abstractions and object representation. Consistent JSON POSTing over HTTPS doesn't care if you have a ${noun}_${verb} method or if you have a ${this_method_does_not_fit_anywhere} method. But it is always JSON POSTing over HTTPS. Should that random this_method_does_not_fit_anywhere method be a GET, a POST, a PUT, or a DELETE? - maybe it is just a method without object representation. Worse, what if my method updated *and* deleted a resource - which REST verb should I use? What a bout an import method that doesn't follow our normal and possibly even manages multiple resources at once? Consistent JSON POSTing over HTTPS does not care what your methods are. Conversly, you're not really REST unless you follow object abstractions to the letter.

The only real benefits to REST are the ability to pull GET resources (assuming there is no authentication) directly from a browser (which you typically are not doing), and the ability to have a tiered system with caching based on URL alone (which I have never seen any company actually do in practice).

I stand by the advice to always use JSON POSTing over HTTPS and to avoid REST. REST does not give you the benefits you think it does - but REST does put more work on the client consumers of your API.

my @a=qw(random brilliant braindead); print $a[rand(@a)];

Replies are listed 'Best First'.
Re^4: modern ways of doing web services - avoid REST too
by Your Mother (Archbishop) on Sep 28, 2013 at 18:55 UTC

    I appreciate the simplification and see you're passionate about it.

    I only add: blaming crummy REST implementations on REST instead of the dev is a bit like blaming obesity on good food and exercise. REST does, like test driven development and other "cost up front" approaches, take discipline. I think it's worth it. I think the benefits cascade. I think working with HTTP instead of at right angles of convenience would have got web development as a whole further faster.

      Thank you for your follow up.

      FWIW - I will gladly take REST over pretty much anything that isn't "always JSON POST over HTTPS." Connecting to REST is very easy compared to most other protocols.

      my @a=qw(random brilliant braindead); print $a[rand(@a)];
Re^4: modern ways of doing web services - avoid REST too
by Anonymous Monk on Sep 27, 2013 at 21:02 UTC

    the consistent POST over HTTPS wins because the URL includes the noun AND the verb. REST cares too much about your abstractions and object representation

    hilarious

      If you are revealing your abstractions (like REST requires), always POSTing of HTTPS wins because the noun and verb are kept together. However, always POSTing over HTTPS does not require that you always have a noun and a verb.

      So if you are playing by RESTs rules, always POSTing over HTTPS wins. But always POSTing over HTTPS wins because you don't have to reveal your abstractions.

      You seem to be an easily amused guy who picks the contexts he likes while ignoring the larger picture.

      my @a=qw(random brilliant braindead); print $a[rand(@a)];

        You seem to be an easily amused guy who picks the contexts he likes while ignoring the larger picture.

        You seem to have a miserable impression of REST , REST is not bondage -- GET/POST -- yeah, thats what html forms support, that is REST -- even if you go full vocabulary because its easy if you're using jQuery for JSON/POSTing/over-HTTPs, it is consistent , you should be GETing/POSTing 90% of the time

        REST is easy going and as consistent as you want it to be

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1056003]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (8)
As of 2024-03-28 09:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found