REST API design: how to handle resources that can

2019-07-23 04:42发布

问题:

I have to put a (read-only) REST service atop of an existing product database. The easy part is having a top level product resource, like:

/api/products/

Now, actually callers of this service will rather need to get their relevant products based on the ID of a store and of a specific process (like "retail"). Behind the scenes, the combination of those two values results in a configured subset of products. This must be transparent for the caller, it should not need to know about these "product portfolios".

So I thought about designing the URI like this, where 1234 is the StoreID and retail is the process:

/api/stores/1234/retail/products

The first question that comes up here is if I should return full products here or URIs to their individual resources on /api/products/ ... the pro would be clearly that the caller does not need to retrieve each individual product from /api/products, the con would be that this would cause a caching headache on the /api/stores/1234/retail/products URI.

To complicate things, those products of course also have prices. Also here, a product does not have one price, but multiple ones that is also dependent of the StoreID and the Process, besides other factors. In reality, prices are direct children of products, so:

/api/products/ABCD/prices

would be the obvious choice, but again, as StoreID and Process are relevant to pre-filter the prices, an URI like:

/api/stores/1234/retail/products/ABCD/prices

would be more appropriate.

At the same time, there are other subresources of products that will not make sense to have under this URI, like product details. Those would clearly only make sense directly under /api/products/ABCD/details as they are not dependant on the store or process.

But this looks somehow messy to me. But at the same time, solving this by only having queryparam filters to solve it directly on the product resource, is not much nicer and does not enforce the caller to provide both, StoreId and process:

/api/products?store=1234&process=retail
/api/products/ABCD/prices?store=1234&process=retail

Even more, process or storeid does not have anything to do with the product, so querying for it directly on product seems odd. For prices, it would make sense, though.

So my question is: is there a good way to solve this that i don't see? And: would you recommend returning full products when they are a subresource - and what do you think about handling (HTTP) caching when doing that?

回答1:

The first question that comes up here is if I should return full products here or URIs to their individual resources on /api/products/ [...] the con would be that this would cause a caching headache on the /api/stores/1234/retail/products URI.

I would definitely return the full products - imagine the amount the client would have to do if it would simply want to display a list of product names. Ideally this endpoint would be paginated (query string can include something like &pageSize=10&pageNumber=2, for example).

Also there are various caching solutions around this - for example you can cache all the products in a data structure service like Redis.

To complicate things, those products of course also have prices [...] and details subresource.

Looking at the Richardson Maturity Model level 3, this would be where links come into play, and you could have something like this under a product resource:

<link rel = "/linkrels/products/ABCD/prices" 
      uri = "/products/ABCD/prices?store=1234&process=retail"/>

and another similar link for the product details resource.

@Roman is right, REST is meant to be discoverable, clients should simply follow links (that can have long/ugly uris), instead of having to memorize them (like in SOAP, for example).