Is it appropriate to mix some sort of action call with a resource URI (e.g. /collection/123?action=resendEmail
)? Would it be better to specify the action and pass the resource id to it (e.g. /collection/resendEmail?id=123
)? Is this the wrong way to be going about it? Traditionally (at least with HTTP) the action being performed is the request method (GET, POST, PUT, DELETE), but those don't really allow for custom actions with a resource.
I'd rather model that in a different way, with a collection of resources representing the emails that are to be sent; the sending will be processed by the internals of the service in due course, at which point the corresponding resource will be removed. (Or the user could DELETE the resource early, causing a canceling of the request to do the send.)
Whatever you do, don't put verbs in the resource name! That's the noun (and the query part is the set of adjectives). Nouning verbs weirds REST!
I use the querystring portion of the URL to filter the set of resources returned when querying a collection (e.g. /collection?someField=someval
). Within my API controller I then determine what kind of comparison it is going to do with that field and value. I've found this really doesn't work. I need a way to allow the API user to specify the type of comparison they want to perform.
The best idea I've come up with so far is to allow the API user to specify it as an appendage to the field name (e.g. /collection?someField:gte=someval
- to indicate that it should return resources where someField is greater than or equal to (>=
) whatever someval
is. Is this a good idea? A bad idea? If so, why? Is there a better way to allow the user to specify the type of comparison to perform with the given field and value?
I'd rather specify a general filter clause and have that as an optional query parameter on any request to fetch the contents of the collection. The client can then specify exactly how to restrict the set returned, in whatever way you desire. I'd also worry a bit about the discoverability of the filter/query language; the richer you make it, the harder it is for arbitrary clients to discover. An alternative approach which, at least theoretically, deals with that discoverability issue is to allow making restriction sub-resources of the collection, which clients obtain by POSTing a document describing the restriction to the collection resource. It's still a slight abuse, but at least it's one you can clearly make discoverable!
This sort of discoverability is one of the things that I find least strong with REST.
I often see URI's that look something like /person/123/dogs
to get the persons dogs. I generally have avoided something like that because in the end I figure that by creating a URI like that you are actually just accessing a dogs collection filtered by a specific person ID. It would be equivalent to /dogs?person=123
. Is there ever really a good reason for a REST URI to be more than two levels deep (/collection/resource_id
)?
When the nested collection is truly a sub-feature of the outer collection's member entities, it is reasonable to structure them as a sub-resource. By “sub-feature” I mean something like UML composition relation, where destroying the outer resource naturally means destroying the inner collection.
Other types of collection can be modeled as an HTTP redirect; thus /person/123/dogs
can indeed be responded to by doing a 307 that redirects to /dogs?person=123
. In this case, the collection isn't actually UML composition, but rather UML aggregation. The difference matters; it is significant!
I don't think there is a proper verb for this action because this transaction isn't really "RESTful." The "s" and "t" stand for "state transfer" and nothing is being transferred here. Or, put another way, by the strictest definition, the verbs like PUT and POST are always used with a noun and "reload" just has the verb.
This reload may not be RESTful, but it may still be useful and you'll just have pick a way to do it and live with or explain that it's unusual. GET is probably the simplest. There's a fair amount of skepticism in the comments, though, so you should think about whether or not this reload action is required because something else isn't quite doing what it should be doing.
Best Answer
POST
doesn't mean "create", it means "process". You can create a new resource by posting a suitable request to an existing resource (i.e. post to/customers
to create a new customer). But you can also usePOST
to fill in all of the other actions which don't correspond to a neat CRUD paradigm.In the case of printing, you should consider the act of printing as a resource itself. You're asking the system to create a "print job" for you. This means you can have a
prints/
resource which acts as the container for all prints requested. When you want to print something youPOST
a document to this resource which contains all the information about the print-out you want to create, identifying the resources you want to print with links to them.As a JSON document, it could look like this:
Obviously, you need to customise this to be relevant to what you want to do. The key thing is that you're identifying other resources to print by specifying their URL.
In response to the request, you could simply return a
200 OK
or a204 No-Content
and treat it as a fire-and-forget process. However, if you wanted to enhance it, you could return201 Created
and specify the URL of the newly created print job, e.g./prints/12345
.A user could then perform a
GET
on the resource to see the status of their print job (pending, in-progress, etc), or could request the job be cancelled by issuing aDELETE
.Once you rephrase the problem in terms of resource, the RESTful design should come naturally and give you opportunity to expand and enhance in ways you may not have immediately considered.