In the first case (shut-down of VMs), I'd consider none of the OP alternatives RESTful. Granted, if you use the Richardson maturity model as a yardstick, they are both leve 2 APIs because they use resources and verbs.
Neither of them, though, use hypermedia controls, and in my opinion, that's the only type of REST that differentiates RESTful API design from RPC. In other words, stick with level 1 and 2, and you're going to have an RPC-style API in most cases.
In order to model two different ways of shutting down a VM, I'd expose the VM itself as a resource that (among other things) contains links:
{
"links": [{
"rel": "shut-down",
"href": "/vms/1234/fdaIX"
}, {
"rel": "power-off",
"href": "/vms/1234/CHTY91"
}],
"name": "Ploeh",
"started": "2016-08-21T12:34:23Z"
}
If a client wishes to shut down the Ploeh
VM, it ought to follow the link with the shut-down
relationship type. (Normally, as outlined in the RESTful Web Services Cookbook, you'd use an IRI or more elaborate identification scheme for relationship types, but I chose to keep the example as simple as possible.)
In this case, there's little other information to provide with the action, so the client should simple make an empty POST against the URL in the href
:
POST /vms/1234/fdaIX HTTP/1.1
(Since this request has no body, it'd be tempting to model this as a GET request, but GET requests should have no observable side-effects, so POST is more correct.)
Likewise, if a client wants to power off the VM, it'll follow the power-off
link instead.
In other words, the relationship types of the links provide affordances that indicate intent. Each relationship type has a specific semantic significance. This is the reason we sometimes talk about the semantic web.
In order to keep the example as clear as possible, I intentionally obscured the URLs in each link. When the hosting server receives the incoming request, it'd know that fdaIX
means shut down, and CHTY91
means power off.
Normally, I'd just encode the action in the URL itself, so that the URLs would be /vms/1234/shut-down
and /vms/1234/power-off
, but when teaching, that blurs the distinction between relationship types (semantics) and URLs (implementation details).
Depending on which clients you have, you may consider making RESTful URLs non-hackable.
CQRS
When it comes to CQRS, one of the few things that Greg Young and Udi Dahan agrees about is that CQRS isn't a top-level architecture. Thus, I'd be cautious about making a RESTful API too CQRS-like, because that'd mean that clients become part of your architecture.
Often, the driving force behind a real (level 3) RESTful API is that you want to be able to evolve your API without breaking clients, and without having control of clients. If that's your motivation, then CQRS wouldn't be my first choice.
While this makes sense for the particular scenario, it's less like the posting of a new resource and more like invoking a method on an object.
RFC 7231 4.3.3
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics.
In short, in the uniform interface of HTTP resources, POST is the catch-all; you use POST if none of the other methods are more suitable
(Always be keeping in mind that the web has been spectacularly successful even though the HTML media type only offers GET and POST).
Does this way of doing things (resources with "methods" as well as data, sort of like OOP in API form) count as RESTful?
How does the client know to send that message-body to that endpoint using that method? One of the key ideas in REST is that the communication of that knowledge happens in band; the client knows to send that message to that endpoint because the representation of application state that was just provided by the server told it to.
In a word: hypermedia.
In other words, think about how you would do this on the web: you would load up some bookmark, which would return you a representation with a bunch of semantically annotated links. You'd follow the shutdown server link, which might return you a representation with a form so that you can choose which server you want. That gets you a representation of the available actions; which includes a link to follow for the reboot action. You follow that link, and it brings you to a form where you can specify the type of the reboot. You submit that form, and the the message for rebooting the system is processed.
The key ideas here are that you never needed to know any of the URI, except for the initial bookmark, and you never needed to know what method of the uniform interface to use, except for the bookmark. All of the rest of the plumbing comes from the representations provided by the server.
Your client (the browser) needed to know the media-type of the representations; to find the data describing the links and methods, and to find the semantic data to provide to you. You yourself needed familiarity with the semantics (what does reboot mean?), so that you could follow the protocol, but you didn't need to know anything about the plumbing.
Fielding, writing in 2008
I should also note that the above is not yet fully RESTful, at least how I use the term. All I have done is described the service interfaces, which is no more than any RPC. In order to make it RESTful, I would need to add hypertext to introduce and define the service, describe how to perform the mapping using forms and/or link templates, and provide code to combine the visualizations in useful ways. I could even go further and define these relationships as a standard, much like Atom has standardized a normal set of HTTP relationships with expected semantics....
In other words, it's not just about the endpoint, but also the journey to find it.
Does this way of doing things (resources with "methods" as well as data, sort of like OOP in API form) count as RESTful?
So using the specific example of this API, I would argue that no, it doesn't count as RESTful. The fact that the response is application/json, a media type that does not define a standard for links, is a big warning sign. Furthermore, it appears that all of the information about the message-body to send to the host is described out of band, rather than within the exchanged representations.
And, even if not, are any of the purported advantages of REST lost in APIs which follow this sort of design?
The primary thing that's lost is the ability to change the resource identifiers and methods used by the uniform interface. For instance, in HTML we can switch a form from GET to POST (or vice versa) simply by making the appropriate change in the representation; we can trivially switch to a different URI namespace because the client is just following the link as represented in the hypermedia.
For example, the Wayback Machine works; a spider can crawl the safe endpoints in your API (and know which ones are safe to crawl just by looking at the representations of the links), gather up the data, replace all of the resource identifiers and make the results available. The client can follow the links in the wayback machine just as easily as it can those in the original API, once provided with the correct starting point.
one could very easily change it to POST /servers/{server_id}/actions/reboot/invocations,although it feels like a bit of a cheat.
REST doesn't care what spelling you use for your identifiers. They are opaque; any meaning encoded into them is done so at the discretion of the server and for its own exclusive use.
As far as REST is concerned, the following request line is entirely consistent with a resource that reboots a server
POST /5dc2a243-f00b-40df-84b2-dbed81d73331 HTTP/1.1
if this sort of pattern was repeated throughout the API, then this would make it RESTful
Yes
even if there are endpoints which look like they are describing verbs or processes or actions rather than nouns/things/resources
Yes.
URI design is a completely different concern from REST.
Best Answer
People often think using POST requests are a solution to CSRF but POST requests are still vulnerable and if CSRF prevention is your goal then you should implement a CSRF token.
However, CSRF is not usually considered a threat to an API because the fundamental premise of a CSRF attack (ie. one site making a request to another without human action) is actually the intention. To give a really simple example, a malicious site which submits a form to mybank.com/transfermoney works because the user already has an authentication cookie set for mybank.com. However, if an AJAX request was made to mybank.com/transfermoney it wouldn't work because the cookie won't be sent and therefore the user won't already be authenticated.
To authenticate with an API you usually have to pass something like an authentication token/key which an attacker would have to know, compared to the other example where the attacker didn't have to know the cookie value to invoke an authenticated action.
With regard to GET vs. POST, if you're trying to achieve a RESTful interface then GET and POST have different functions. GET should be used for retrieving (reading) a resource, whereas POST should be used for submitting a new one.