I am currently build a REST API for a webservice that, amongst other things, handles user bans.
The current API I designed look like:
URL: GET /user/<user_id>/bans/<session_id>
Description: Get the ban on the specified session for the specified user.
Output:
{
session_id: 1,
banned_until: "..."
}
URL: GET /user/<user_id>/bans
Description: Get all bans for the specified user.
Output:
[
{
session_id: 1,
banned_until: "..."
},
{
session_id: 2,
banned_until: "..."
}
]
URL: PUT /user/<user_id>/bans/<session_id>
Description: Set or update a ban on the specified session for the specified user.
Input:
{
session_id: 1,
banned_until: "..."
}
Output:
{
session_id: 1,
banned_until: "..."
}
Now one of my coworkers believes that the API is wrong because, for instance in the case of the PUT, the user has to specify the session_id twice: once in the URL and once in the content and those two have to match (which means an extra check server-side). He makes the same comment for the GET where the user specifies the session id in the URL to get it back in the response, meaning a waste of bandwidth.
While I understand his concerns, I however think the current design as it is, is simpler for the user (only one data structure to care about) and that the extra check on the server isn't that much work to do compared to the comfort it brings the user.
Is there an official guideline regarding this in the REST best-practices ? What is the usual/recommended way of dealing with this ?
Personally, I think what you are talking about is the different between the PUT and POST methods.
From the RESTful CookBook:
Use PUT when you can update a resource completely through a specific resource. For instance, if you know that an article resides at http://example.org/article/1234, you can PUT a new resource representation of this article directly through a PUT on this URL.
So, in the PUT example you gave, I think it is perfectly valid to specify the sessionID in the URL since you are storing the ban at a specific point.
However you could also create a POST service at URL: POST /user/<user_id>/bans to satisfy your co-worker. :-)
Related
I am designing a restful web service to create and read reports made from an app. When creating a report its possible to add some privacy sensitive information with it like a name, phone number, mail etc. After creating the report its made publicly visible through the same web service.
POST /report
{
"name":"test",
"email":"test#example.com",
"report_contents":....
}
returns 200 OK with:
{
"id":1,
"report_contents":....
}
and a method to get said report:
GET /report/{report_id}
I have another app with which an admin can manage the reports created though the previous web service. In this application I would like to display the privacy sensitive information. It uses the following URL to get a specific report.
GET /report/{report_id}
which returns 200 OK:
{
"id":1,
"name":"test",
"email":"test#example.com",
"report_contents":....
}
Now there is the issue. This is the exact same url. Is it Is it possible/conventional or even a good idea to use the same web service for both calls, but have some kind of CRUD management with it where depending on the role of the user a part of the information is not displayed/blocked? Or would it be better to make a separate web service with restrictions?
Yes, it's OK for different representations of the same resource to be returned at the same URL for different requests. That's how content negotiation works.
If you are concerned about this, I can think of two options:
One option is to include a query parameter to make the choice of views explicit, and access can be controlled for each. E.g.
/report/{report_id}?view=full
/report/{report_id}?view=restricted
Or you could also consider two sub-resources, one called /report/{report_id}/full and one called /report/{report_id}/restricted, and then you can return a 40x code when the user doesn't have correct permission, with a Location header as a hint of where they can look.
If your language of choice supports it, you could return a dynamic object.
here's some pseudo code.
if (loggedInUser != isAdmin(user))
return new { id: 1, contents: "..." }
else
return new { id: 1, name: "test", email: "test#test.com", contents: "..." }
Personally, I would have different areas that do different things. One area that retrieves the model for everyone. In the other it'd be like an admin area.
In the one area, you have
We have a web application (AngularJS and Web API) which has quite a simple functionality - displays a list of jobs and allows users to select and cancel selected jobs.
We are trying to follow RESTful approach with our API, but that's where it gets confusing.
Getting jobs is easy - simple GET: /jobs
How shall we cancel the selected jobs? Bearing in mind that this is the only operation on jobs we need to implement. The easiest and most logical approach (to me) is to send the list of selected jobs IDs to the API (server) and do necessary procedures. But that's not RESTful way.
If we are to do it following RESTful approach it seams that we need to send PATCH request to jobs, with json similar to this:
PATCH: /jobs
[
{
"op": "replace",
"path": "/jobs/123",
"status": "cancelled"
},
{
"op": "replace",
"path": "/jobs/321",
"status": "cancelled"
},
]
That will require generating this json on client, then mapping it to some the model on server, parsing "path" property to get the job ID and then do actual cancellation. This seems very convoluted and artificial to me.
What is the general advice on this kind of operation? I'm curious what people do in real life when a lot of operations can't be simply mapped to RESTful resource paradigm.
Thanks!
If by cancelling a job you mean deleting it then you could use the DELETE verb:
DELETE /jobs?ids=123,321,...
If by cancelling a job you mean setting some status field to cancelled then you could use the PATCH verb:
PATCH /jobs
Content-Type: application/json
[ { "id": 123, "status": "cancelled" }, { "id": 321, "status": "cancelled" } ]
POST for Business Process
POST is often an overlooked solution in this situation. Treating resources as nouns is a useful and common practice in REST, and as such, POST is often mapped to the "CREATE" operation from CRUD semantics - however the HTTP Spec for POST mandates no such thing:
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. For example, POST is used for the following functions (among others):
Providing a block of data, such as the fields entered into an HTML form, to a data-handling process;
Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group of articles;
Creating a new resource that has yet to be identified by the origin server; and
Appending data to a resource's existing representation(s).
In your case, you could use:
POST /jobs/123/cancel
and consider it an example of the first option - providing a block of data to a data handling process - and is analogous to html forms using POST to submit the form.
With this technique, you could return the job representation in the body and/or return a 303 See Other status code with the Location set to /jobs/123
Some people complain that this looks 'too RPC' - but there is nothing that is not RESTful about it if you read the spec - and personally I find it much clearer than trying to find an arbitrary mapping from CRUD operations to real business processes.
Ideally, if you are concerned with following the REST spec, the URI for the cancel operation should be provided to the client via a hypermedia link in your job representation. e.g. if you were using HAL, you'd have:
GET /jobs/123
{
"id": 123,
"name": "some job name",
"_links" : {
"cancel" : {
"href" : "/jobs/123/cancel"
},
"self" : {
"href" : "/jobs/123"
}
}
}
The client could then obtain the href of the "cancel" rel link, and POST to it to effect the cancellation.
Treat Processes as Resources
Another option is, depending on if it makes sense in your domain, to make a 'cancellation' a noun and associate data with it, such as who cancelled it, when it was cancelled etc. - this is especially useful if a job may be cancelled, reopened and cancelled again, as the history of changes could be useful business data, or if the act of cancelling is an asynchronous process that requires tracking the state of the cancellation request over time. With this approach, you could use:
POST /jobs/123/cancellations
which would "create" a job cancellation - you could then have operations like:
GET /jobs/123/cancellations/1
to return the data associated with the cancellation, e.g.
{
"cancelledBy": "Joe Smith",
"requestedAt": "2016-09-01T12:43:22Z",
"status": "in process"
"completedAt": null
}
and:
GET /jobs/123/cancellations
to return a collection of cancellations that have been applied to the job and their current status.
Example 1: Let’s compare it with a real-world example: You go to a restaurant you sit at your table and you choose that you need ABC. You will have your waiter coming up and taking a note of what you want. You tell him that you want ABC. So, you are requesting ABC, the waiter responds back with ABC he gets in the kitchen and serves you the food. In this case, who is your interface in between you and the kitchen is your waiter. It’s his responsibility to carry the request from you to the kitchen, make sure it’s getting done, and you know once it is ready he gets back to you as a response.
Example 2: Another important example that we can relate is travel booking systems. For instance, take Kayak the biggest online site for booking tickets. You enter your destination, once you select dates and click on search, what you get back are the results from different airlines. How is Kayak communicating with all these airlines? There must be some ways that these airlines are actually exposing some level of information to Kayak. That’s all the talking, it’s through API’s
Example 3: Now open UBER and see. Once the site is loaded, it gives you an ability to log in or continue with Facebook and Google. In this case, Google and Facebook are also exposing some level of users’ information. There is an agreement between UBER and Google/Facebook that has already happened. That’s the reason it is letting you sign up with Google/ Facebook.
PUT /jobs{/ids}/status "cancelled"
so for example
PUT /jobs/123,321/status "cancelled"
if you want to cancel multiple jobs. Be aware, that the job id must not contain the comma character.
https://www.rfc-editor.org/rfc/rfc6570#page-25
Let's say I have a REST API adhering to basic HATEOAS principles. Items belong to a User.
GET /item/13
{
id: 13,
name: 'someItem',
type: 'someType',
_links: [
{
rel: 'user',
href: '/user/42'
}
]
}
Now I need a way to change the user for a given item. Using either a PUT or a PATCH, which is the preferable way of performing that modification?
Establish the new relation by setting the id of the new linked resource as a simple property in the JSON body
PATCH /item/13
{
userId: 43
}
Establish the new relation by having the client pass the link itself as the input
PATCH /item/13
{
_links: [
rel: 'user',
href: '/user/43'
]
}
I usually think of links as read-only representations of relations stored in other formats (such as id:s to other resources), returned from GET calls. It doesn't feel very natural to me to have links as input to POST/PUT/PATCH calls, and the fact that links is an array makes it even stranger (should you be able to update all links? One single link?), but I have seen it suggested in various articles. Is there a best practice? What would be the benefits of using the links approach?
The point of REST is (at least one of them) is to make everything visible through a standard interface. In other words, if the 'relations' are a thing, than it too should have its own resource.
The API should also be more descriptive. This might be subjective, and I don't know all the details of your model/design, but 'items' don't have 'links'. 'Items' might instead have a single 'owner'. If this is the case, it might look something like:
GET /item/123/owner
So POSTing or PUTing an URL of a user (or some simple representation) would 'change' the owner of the item. It might be not allowed to DELETE the owner, depending on if the model allows unowned items.
Note, that the representation under "/item/123" would in this case have to link to "/item/123/owner", since the client only follows links it gets from the server.
So, think about what are important 'things', all of those should have a resource. Also, try to add as much 'meaning'/semantics as you can. The relation should not be called 'user', it should be called 'owner' (or whatever the meaning should be in your model).
Say we have the following server resource:
api.example.com/event/1
Which returns some arbitrary resource, say:
{
id: 1,
details: {
type: 'webinar',
....
},
attendees: [
{
user_id: 1,
first_name: 'Bob'
...
},
...
]
}
It might be useful for a client to make a request to get just the event details of the event but not the list of attendees.
Is it better to provided two separate URLs for the resources and force two separate requests if a client wants both resources?
api.example.com/event/{event_id}
api.example.com/attendees/{event_id}
Or is it better to offer the same two endpoints, but optionally have the first one support a GET param to toggle the attendee listing on or off
api.example.com/event/{event_id}?listAttendees={true|false}
api.example.com/attendees/{event_id}
Where the listAttendees parameter will either have the representation return the attendee list or not.
Is it an common practice to allow GET params to change the representation returned from a specific URL?
I'd say the most correct way to do that in REST would be with different media-types, or media-type parameters, but since most people don't use custom media-types, I often use something I call the zoom protocol. The idea is that you have a zoom or expand parameter, with a numeric value, and it recursively includes the children entities, decreasing the parameter until it reaches zero.
So, a request like:
GET api.example.com/event/1
Returns the plain representation for the event resource, without embedding anything. A request like:
GET api.example.com/event/1?zoom=1
Would include the immediate children of event, in your case, the atendees. Following on that:
GET api.example.com/event/1?zoom=2
Would include the immediate children of event, the immediate children of atendees.
To answer your question, in REST the whole URI is an atomic identifier, so the parameters are part of the URI. That can be a problem if you're using something that won't interpret URIs in the same way, like old cache servers who won't cache URIs with a querystring.
I'm working a REST-ful API in which resources which are fairly interrelated. Resources reference each other, and these references may be created or deleted. I'm a little uncertain how to support associating resources together when they reference each other using hyperlinks.
A simple example follows with two resources, A and B.
Resource A:
name: integer
list_b: [list of resource B]
Resource B:
id: integer
description: String
Now, A does not include B in its document, but rather links to it. When using hypermedia, it might look something like this:
Resource A:
{
id: 1,
list_b: [
{ id: 1, href: "https://server/api/b/1" },
{ id: 2, href: "https://server/api/b/2" }
]
}
If a user wants to add or delete one of the B references in A's list, how do they do so, taking into account the presence of the hyperlink? I want the user to be able to update the entire A resource in one PUT operation, but nothing in the output indicates which value for B is required. It make sense to me for the user to perform PUT with content like this:
Resource A:
{
id: 1,
list_b: [
{ id: 1, href: "https://server/api/b/1" },
{ id: 2, href: "https://server/api/b/2" },
{ id: 3 },
]
}
and receive the updated resource (in the response) like this:
Resource A:
{
id: 1,
list_b: [
{ id: 1, href: "https://server/api/b/1" },
{ id: 2, href: "https://server/api/b/2" },
{ id: 3, href: "https://server/api/b/3" }
]
}
My concern is that the user won't necessarily know what to include in the resource when updating resource A's list_b.
When dealing with hyperlinks from one resource to another, how should creates and updates work? Should clients be allowed to update part of the link (the id), or should they be required to update both parts of the link?
Note: I know another approach might be exposing a sub-url for resource A. It could expose list_b as a resource which is operable via HTTP (allowing clients to use POST, PUT, and DELETE on the list resource itself). But this seems less reasonable when A contains multiple references to other resource types. Each field which references another would potentially require a sub-url, which, if there are 10+ fields, is unwieldy, and requires multiple HTTP requests to update the resource.
HATEOAS connects resources together in a RESTful interface, and it's not clear here whether or not the subsidiary objects you're describing really make sense as independent resources. The "AS" part of HATEOAS reminds us of the role that Web pages play as "resources" in a Web application. Each Web page is really an interactive representation of application state (the "application" in this case being a classical, multiple-page Web application), and the hyperlinks to other resources provide the user with transitions to other application states.
A RESTful Web API, having JavaScript code rather than human beings as its client, is naturally data-access-oriented, so few if any of its resources take the form of "application state," per se. In a tradition Web application, you can draw a state transition diagram and clearly see the connections among states, and thus among resources. In a RESTful API, the boundaries among passive data resources are motivated more by the efficiencies of client/server interactions and other subtle forces.
So do your subsidiary objects ("B") here really need to be represent as first-class resources? Are there instances where the front end will enumerate or otherwise access them independent of the aggregates in which they participate ("A")?
If the answer is "no," then they obviously shouldn't be represented hyptertextually in the "A" structure. I presume that the answer is "yes," however, and that you also have good reason to offer all of the other subsidiary objects to which you refer as independent resources. In this case, there's some amount of interface work in the form of routes and controllers that is necessary to support all of those resources no matter what, because your application is presumably providing a means to manipulate them each on their own, or at least query them (through hyperlinks such as those in your example).
This being the case, a POST to the path representing your collection of "B" objects (e.g., "server/api/b") can return a URL in the response's "location" header value as POSTs that create new resources are supposed to do. When your user interactively adds a new "B" to a list belonging to an "A" on your Web page, your front end can first POST the new "B," getting its URL back through the location header on success. It can then incorporate that link into the list representation inside its "A" object before PUTting the updated "A."
The ID value is a bit of a wrinkle, as you'll be tempted to break the encapsulation of the back end by extracting the ID value from the text of the URL. True HATEOAS zealots make their RESTful APIs produce obfuscated, hashed or otherwise unintelligible URLs specifically to frustrate such encapsulation-breaking on the part of clients. Better that the POST of the new "B" object returns a complete representation of the new "B" object, including its ID, in its response body, so that the client can reconstitute the full object and extract the ID from it, thus narrowing the coupling to the resource itself and not the details of the RESTful interface through which it is obtained.
You should also look at the LINK method:
LINK /ResourceA/1 HTTP/1.1
Link: <http://example.com/ResourceB/3>; rel="list_b"
...
204 Yeah Fine, Whatever
This tells /ResourceA/1 to link to /ResourceB/3 using the relationship "list_b".