We have a URL in the following format
/instance/{instanceType}/{instanceId}
You can call it with the standard HTTP methods: POST, GET, DELETE, PUT. However, there are a few more actions that we take on it such as "Save as draft" or "Curate"
We thought we could just use custom HTTP methods like: DRAFT, VALIDATE, CURATE
I think this is acceptable since the standards say
"The set of common methods for HTTP/1.1 is defined below. Although this set can be expanded, additional methods cannot be assumed to share the same semantics for separately extended clients and servers."
And tools like WebDav create some of their own extensions.
Are there problems someone has run into with custom methods? I'm thinking of proxy servers and firewalls but any other areas of concern are welcome. Should I stay on the safe side and just have a URL parameter like action=validate|curate|draft?
-
6As I quote from RFC 1925 again - "In protocol design, perfection has been reached not when there is nothing left to add, but when there is nothing left to take away." -- if it works, there is no reason to add to http.user40980– user4098004/04/2013 02:29:05Commented Apr 4, 2013 at 2:29
-
4Nothing wrong, as long as you realize that you're now using a custom protocol and not HTTP.user16764– user1676404/04/2013 23:26:51Commented Apr 4, 2013 at 23:26
-
23@user16764 "The set of common methods for HTTP/1.1 is defined below. Although this set can be expanded, additional methods cannot be assumed to share the same semantics for separately extended clients and servers." w3.org/Protocols/rfc2616/rfc2616-sec9.html Therefore it is allowed, and it is still HTTPRuan Mendes– Ruan Mendes04/05/2013 01:54:10Commented Apr 5, 2013 at 1:54
-
imho there's nothing to add/remove from HTTP since the method definitions state that using custom methods are already acceptable within the HTTP/1.1 scope but cant be expected to share the same semantics, so I guess the points from both @MichaelT and Juan Mendes can be somewhat appeasedProf– Prof12/03/2015 08:54:48Commented Dec 3, 2015 at 8:54
-
Consider using the standard HTTP methods, and incorporating your desired custom method names into the resources URL. For example, following the scheme from cloud.google.com/apis/design/custom_methodsuser7610– user761010/18/2021 07:35:28Commented Oct 18, 2021 at 7:35
3 Answers 3
One of the fundamental constraints of HTTP and the central design feature of REST is a uniform interface provided by (among other things) a small, fixed set of methods that apply universally to all resources. The uniform interface constraint has a number of upsides and downsides. I'm quoting from Fielding liberally here.
A uniform interface:
- is simpler.
- decouples implementations from the services that they provide.
- allows a layered architecture, including things like HTTP load balancers (nginx) and caches (varnish).
On the other hand, a uniform interface:
- degrades efficiency, because information is transferred in a standardized form rather than one which is specific to an application's needs.
The tradeoffs are "designed for the common case of the Web" and have allowed a large ecosystem to be built which provides solutions to many of the common problems in web architectures. Adhering to a uniform interface will allow your system to benefit from this ecosystem while breaking it will make it that difficult. You might want to use a load balancer like nginx but now you can only use a load balancer that understands DRAFT and CURATE. You might want to use an HTTP cache layer like Varnish but now you can only use an HTTP cache layer that understands DRAFT and CURATE. You might want to ask someone for help troubleshooting a server failure but no one else knows the semantics for a CURATE request. It may be difficult to change your preferred client or server libraries to understand and correctly implement the new methods. And so on.
The correct* way to represent this is as a state transformation on the resource (or related resources). You don't DRAFT a post, you transform its draft
state to true
or you create a draft
resource that contains the changes and links to previous draft versions. You don't CURATE a post, you transform its curated
state to true
or create a curation
resource that links the post with the user that curated it.
* Correct in that it most closely follows the REST architectural principles.
-
Thanks for the comments on load balancing, I'll definitely look at that. Do you know of a resource that states whether custom methods are acceptable or not?Ruan Mendes– Ruan Mendes04/04/2013 01:22:18Commented Apr 4, 2013 at 1:22
-
2I don't see any advantage to custom methods unless they are a part of a widely supported extension like WEBDAV (and even then, not so much) so I have never looked into it. I would just recommend that you treat these changes as state transformations. The web works just fine with the methods we already have. There's really no good reason to add more unless they make sense as a part of the uniform interface (like PATCH).Rein Henrichs– Rein Henrichs04/04/2013 01:24:03Commented Apr 4, 2013 at 1:24
-
6I see the advantage in designing the way you want your HTTP service to work for yourself. however "additional methods cannot be assumed to share the same semantics" - Fair enough, BUT it is still part of the HTTP/1.1 scope, therefore firewalls, proxies, load balancers and the like should be allowing for this possibility, if they don't then are they not implementing HTTP/1.1 properly?Prof– Prof12/03/2015 09:17:48Commented Dec 3, 2015 at 9:17
-
You're probably right from the resty POV, however, I can't see why custom verbs should be any problem. All the tools should treat them just like POST, i.e., "the resource probably changes and that's all we know".maaartinus– maaartinus05/05/2018 01:44:26Commented May 5, 2018 at 1:44
I think custom HTTP method is the very best way to implement entity actions. Adding the action to the entity body (POST) doesn't seems right, it's not part of your entity (although the result might be saved in it). Also, using the custom HTTP methods proxies could determine their actions without the need of parsing the entity body.
It's like CRUD, you'd always want to implement those, but also you have your own specific set of actions (per enitity). I really don't see what would be the problem extending those.
Also @Rein Henrichs "You don't DRAFT a post, you transform its draft state to true or you create a draft resource" seems false to me. A drafts
property would be used for persistent saving the state, not for making the transformation. Actions don't even necessarily result in a 'state', or be saved in a property. Creating a seperate entity for each state/transformation seems even more fuzzy.. try to maintain the same reference (URI) to the enity.
-
6This is a fair point albeit disagreed widely, I can see the reasoning behind it and I don't agree with the downvote (especially with no comment from the voter). Let's take PHP exception handling as an example, "Best Practice" seems to lean toward using specific Exception types to suggest the kind of exception, even ignoring the actual message like RuntimeException vs BadMethodCallException. So why is it so widely argued against using DRAFT if using custom methods is already considered part of the HTTP/1.1 scope? And load balancers and proxies should really be accepting this possibility as wellProf– Prof12/03/2015 09:12:29Commented Dec 3, 2015 at 9:12
I would prefer to design these as subresources, onto which you perform a POST request.
Consider you have a resource at /instance/type/1
, I would have the representation of that resource convey a couple of links to 'actions' that can be performed on the resource, such as /instance/type/1/draft
and /instance/type/1/curate
. In JSON, this could be as simple as:
{
"some property":"the usual value",
"state": "we can still inform the client about the current state",
"draft": "http://server/instance/type/1/draft",
"curate": "http://server/instance/type/1/curate"
}
This allows the client to be very explicit about what needs to happen, during the POST request to the link provided by the curate
member. The resource posted there could include arguments that detail the event that would, perhaps, inflict a state transition.
Going with the 'naive' approach of moving between the possible states on a resource has the disadvantage of not capturing what events led to these transitions.
State transitions usually occur in response to specific events, and I'd rather capture those events than let the client decide that something is now in a specific 'state'. It also makes validation a lot harder. Plus, you wouldn't be able to capture any 'arguments' unless you describe those in the state itself as well. And then it gets all icky when some code changes those without real state transition, and the validation required, and the whole thing quickly becomes a mess.
-
Good answer. Being able to provide arguments to state-transitions & having the server encapsulate and manage these, is by far the best approach.Thomas W– Thomas W06/28/2016 03:17:21Commented Jun 28, 2016 at 3:17
-
The company I'm currently at (VMware) does this. A GET on
/vms/some-id
returns links to actions likePOST /vms/some-id/restart
and we use it to determine if actions should be enabled or disabled. I have a love/hate relationship with HATEOAS :)Ruan Mendes– Ruan Mendes05/08/2018 11:36:46Commented May 8, 2018 at 11:36 -
1It would make so much more sense if the action being taken was the verb of the request versus some random query parameter, resource path segment or body property.Matthew Whited– Matthew Whited08/08/2019 13:25:29Commented Aug 8, 2019 at 13:25
-
You can't link to a verb.Dave Van den Eynde– Dave Van den Eynde10/02/2019 04:32:25Commented Oct 2, 2019 at 4:32
-
You shouldn't link to a post, put or delete either.Matthew Whited– Matthew Whited03/19/2020 19:32:03Commented Mar 19, 2020 at 19:32