Reputation: 4628
My application has a resource at /foo
. Normally, it is represented by an HTTP response payload like this:
{"a": "some text", "b": "some text", "c": "some text", "d": "some text"}
The client doesn't always need all four members of this object. What is the RESTfully semantic way for the client to tell the server what it needs in the representation? e.g. if it wants:
{"a": "some text", "b": "some text", "d": "some text"}
How should it GET
it? Some possibilities (I'm looking for correction if I misunderstand REST):
GET /foo?sections=a,b,d
.
GET /foo/a+b+d
My favorite if REST semantics doesn't cover this issue, because of its simplicity.
/widgets
representing a presentable list of /widget/<id>
resources, which I've never had a problem with.GET /foo/a
, etc, and have the client make a request per component of /foo
it wants.
/foo
has hundreds of components and the client needs 100 of those./foo
, I have to use Ajax, which is problematic if I just want a single HTML page that can be crawled, rendered by minimalist browsers, etc./foo
: {"a": {"url": "/foo/a", "content": "some text"}, ...}
GET /foo
, Content-Type: application/json
and {"sections": ["a","b","d"]}
in the request body.
GET
. It's legal HTTP but how can I guarantee some user's proxy doesn't strip the body from a GET
request?GET
request so I can't use that for testing.Sections-Needed: a,b,d
POST /foo/requests
, Content-Type: application/json
and {"sections": ["a","b","d"]}
in the request body. Receive a 201
with Location: /foo/requests/1
. Then GET /foo/requests/1
to receive the desired representation of /foo
/foo/requests/1
is just an alias that would only be used once and only kept until it is requested.Upvotes: 65
Views: 20605
Reputation: 1408
Actually it depends on the functionality of the resource. If for example the resource represents an entity:
/customers/5
Here the '5' represents an id of the customer
Response:
{
"id": 5,
"name": "John",
"surename": "Doe",
"marital_status": "single",
"sex": "male",
...
}
So if we will examine it closely, each json property actually represents a field of the record on customer resource instance. Let's assume consumer would like to get partial response, meaning, part of the fields. We can look at it as the consumer wants to have the ability to select the various fields via the request, which are interesting to him, but not more (in order to save traffic or performance, if part of the fields are hard to compute).
I think in this situation, the most readable and correct API would be (for example, get only name and surename)
/customers/5?fields=name,surename
Response:
{
"name": "John",
"surename": "Doe"
}
fields=id,name
or fields=name,id
), although the response is the same, those responses will be cached separately.Upvotes: 5
Reputation: 4628
I've decided on the following:
Supporting few member combinations: I'll come up with a name for each combination. e.g. if an article has members for author, date, and body, /article/some-slug
will return all of it and /article/some-slug/meta
will just return the author and date.
Supporting many combinations: I'll separate member names by hyphens: /foo/a-b-c
.
Either way, I'll return a 404
if the combination is unsupported.
From the definition of REST:
a resource R is a temporally varying membership function MR(t), which for time t maps to a set of entities, or values, which are equivalent. The values in the set may be resource representations and/or resource identifiers.
A representation being an HTTP body and an identifier being a URL.
This is crucial. An identifier is just a value associated with other identifiers and representations. That's distinct from the identifier→representation mapping. The server can map whatever identifier it wants to any representation, as long as both are associated by the same resource.
It's up to the developer to come up with resource definitions that reasonably describe the business by thinking of categories of things like "users" and "posts".
If I really care about perfect HATEOAS, I could put a hyperlink somewhere in the /foo
representation to /foo/members
, and that representation would just contain a hyperlink to every supported combination of members.
From the definition of a URL:
The query component contains non-hierarchical data that, along with data in the path component, serves to identify a resource within the scope of the URI's scheme and naming authority (if any).
So /foo?sections=a,b,d
and /foo?sections=b
are distinct identifiers. But they can be associated within the same resource while being mapped to different representations.
HTTP's 404
code means that the server couldn't find anything to map the URL to, not that the URL is not associated with any resource.
No browser or cache will ever have trouble with slashes or hyphens.
Upvotes: 7
Reputation: 2288
you could use a second vendor media-type in the request header application/vnd.com.mycompany.resource.rep2, you can't bookmark this however, query-parameters are not cacheable (/foo?sections=a,b,c) you could take a look at matrix-parameters however regarding this question they should be cacheable URL matrix parameters vs. request parameters
Upvotes: 2
Reputation: 34200
I would suggest the querystring solution (your first). Your arguments against the other alternatives are good arguments (and ones that I've run into in practise when trying to solve the same problem). In particular, the "loosen the constraints/respond to foo/a
" solution can work in limited cases, but introduces a lot of complexity into an API from both implementation and consumption and hasn't, in my experience, been worth the effort.
I'll weakly counter your "seems to mean" argument with a common example: consider the resource that is a large list of objects (GET /Customers
). It's perfectly reasonable to page these objects, and it's commonplace to use the querystring to do that: GET /Customers?offset=100&take=50
as an example. In this case, the querystring isn't filtering on any property of the listed object, it's providing parameters for a sub-view of the object.
More concretely, I'd say that you can maintain consistency and HATEOAS through these criteria for use of the querystring:
However, what to return for these Uris can sometimes pose more complex questions:
/foo
is an entity but foo/a
is a string); the alternative is to return a partially-populated entity/foo
doesn't have an a
, a 404
status is misleading (/foo
does exist!), but an empty response may be equally confusinga
is mandatory but the client requests only b
, you are forced to return either a junk value for a
, or an invalid object)In the past, I have tried to resolve this by defining specific named "views" of required entities, and allowing a querystring like ?view=summary
or ?view=totalsOnly
- limiting the number of permutations. This also allows for definition of a subset of the entity that "makes sense" to the consumer of the service, and can be documented.
Ultimately, I think that this comes down to an issue of consistency more than anything: you can meet HATEOAS guidance using the querystring relatively easily, but the choices you make need to be consistent across your API and, I'd say, well documented.
Upvotes: 14
Reputation: 1500
If a,b,c are property of a resource like admin for role property the right way is to use is the first way that you've suggested GET /foo?sections=a,b,d
because in this case you would apply a filter to the foo
collection. Otherwise if a,b and c are a singole resource of foo
collection the the way that would follow is to do a series of GET
requests /foo/a /foo/b /foo/c
. This approach, as you said, has a high payload for request but it is the correct way to follow the approach Restfull. I would not use the second proposal made by you because plus char in a url has a special meaning.
Another proposal is to abandon use GET and POST and create an action for the foo
collection like so: /foo/filter
or /foo/selection
or any verb that represent an action on the collection. In this way, having a post request body, you can pass a json list of the resource you would.
Upvotes: 1