RESTful URLs for distinct versions of a resource

流过昼夜 提交于 2021-02-06 20:05:57

问题


I'm having a real hard time conceptually understanding something proper concerning URLs for versioned resources.

Let's say I have an application that tracks recipes in a way very similar to a version control system, like old school like RCS or something. Each version can be a working copy for some time then have a new version created from it. Each version has comments associated with it and doesn't share comments. I can go back historically at any time and look at the recipe as it evolved, but each instance is always considered a version of the same recipe. I'm trying to figure out the most appropriate way to construct URLs to reference these and I'm having trouble understand some of the differences between like subresources and temporal resources and such.

The two main ways I've seen this done are:

> 1) query parameters
> -- /recipes/ultimate-thing                -> List of available versions of Ultimate Thing
> -- /recipes/ultimate-thing?version=2      -> Version 2 of Ultimate Thing
> -- /recipes/ultimate-thing?version=latest -> Current working version of Ultimate Thing

2a) Nested resources with versions considered subresources
-- /recipes/ultimate-thing/versions/      -> List of available versions of Ultimate Thing
-- /recipes/ultimate-thing/versions/2     -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing                -> Current working version of Ultimate Thing

2b) Nested resources with the list at the resource
-- /recipes/ultimate-thing                 -> List of available versions of Ultimate Thing
-- /recipes/ultimate-thing/versions/2      -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing/versions/latest -> Current working version of Ultimate Thing

I feel like everytime I try to convince myself of one way of doing it I feel like I lack the understand to construct a proper justification for that method.

1 seems to be preferred by a lot of rails folks, but I'm not sure how to be clear about when I'd like to create a new version of the recipe since POST to a specific resource (/recipes/ultimate-thing) is usually wanted to create the resource with that name if it doesn't exist, would it be appropriate to return something like a 404 instead of creating it and only post to /recipes to create new recipes and then allow POSTs to /recipes/ultimate-thing to create a new version? Also, how would comments be represented? Without version I could do something like /recipes/ultimate-thing/comments, but /recipes/utlimate-thing/comments?version=latest seems ugly, while 2b does seem more explicit "Comments of the latest version of the ultimate thing recipe", 2a seems the cleanest.

In fact, I generally like 2a the best but I'm having trouble figuring that versions are subresources of recipes since there are no recipes, a version is the recipe but all the versions of that recipes should be logically grouped together.

I also saw 2b here on stackoverflow but it seems like it might be overly verbose since you'd need something like /recipes/ultimate-thing/version/latest/comments all the time. Though this does make the most sense if I think of a version as a collection of ingredients with instructions that can be annotated and a recipe is a collection of versions sharing a similar intent.

I really like 2a, but is there something with the approach that I'm missing that makes it a bad idea? Am I missing something with how 1 would work that makes it make more sense that it seems preferred by a lot of people?

Or would it make even more sense to always do something like /recipes/ultimate-thing/latest or /recipes/ultimate-thing/2/comments even though that makes it the least explicit that these are different revisions of the same recipe?

I'm not sure if this is a super-appropriate forum for this, I'd just like to have discussion about it so I understand why one approach would be better than another.


回答1:


Wow. Good question. I too find that one of the biggest challenges of REST is that it is a style, not a strict specification. The style seems not to be well-understood, and there is nothing that enforces it.

Short answer: Style #1: /recipes/cake?version=2

This style seems most appropriate to me. It was only after a quick Google search that I found that query parameters are considered part of the URI. Thus it seems appropriate to put an identifying attribute in the URI.

Styles 2 and 3 seems to violate other REST conventions where path segments represent IDs and sub-resources. Thus the style /recipes/cake/2 looks like the "cake" resource with ID 2. /recipes/cake/version/2 makes "version" appear to be a sub-resource, when in fact it is an attribute of the parent resource.




回答2:


I just implemented this pattern late last year, and followed a combination of 2a and 2b:

2c) Nested resources with versions considered subsets
-- /recipes/ultimate-thing/versions/      -> List of available versions of Ultimate Thing
-- /recipes/ultimate-thing/versions/2     -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing/versions/latest-> Redirect to current working version of Ultimate Thing

Don't think of longer URI's as "subresources" or "attributes", as if a URI identifies a thing. It's much more natural to treat a URI as identifying data, and, since HTTP URI's are hierarchical, longer URI's refer to subsets of data. An item in a collection is data: a subset of all the data in the collection. An "attribute" of a "thing" is data: a subset of all the data about that thing. A "subresource" is data about a "thing" that does not exist independently of its parent, and therefore is a subset of all the data about the parent.

In your case, you don't have to perform too much mental judo to think of recipes/cherry-pie/ as all the data about what a cherry pie is and how to make one, rather than a specific version of the instructions. That includes identity info and other non-version-specific data, but also a link to versions/, and links to the history and funny videos where someone gets one in the face. The URI recipes/cherry-pie/versions/ is then the subset of all cherry pie data which is versioned; in general, it is nothing more than a collection of links to each URI of the form recipes/cherry-pie/versions/{version}/. I would also include in versions/ a documented link to recipes/cherry-pie/versions/latest/, which does a temporary redirect to recipes/cherry-pie/versions/182/ or whatever the latest version is.

This works very naturally for clients. Lots of people trip up on what they see as an explosion of resources, and think the poor client can't possibly keep up. But if you divide your resources along natural user workflow boundaries, you won't have a problem. Some clients will want to browse a set of recipes; some will drill down to a single recipe (regardless of version); some will drill down to want to know how many versions there are, which one is the latest, and will select from among them; some will drill down and retrieve a single version. If you want to show all the versions for a single recipe, or for all recipes, you're probably doing something wrong (in the rare case you're not, design another resource, like recipes/byname/pie/quickview, which collects all the data and caches it heavily).




回答3:


It all depends on how are you looking at the resource versions. Would these be just used for historical tracking like in wiki or the versions are separate resources. In the first case the EJK answer seems like a good solution. For the second case creating new unique identifier for the resource versions/variations could be another solution. For example:

3) Resources with versions
-- /recipes/                          -> List of available recipes versions
-- /recipes/ultimate-thing-version-2  -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing            -> The "offical" version of Ultimate Thing

This is even more prefered solution if the resources are variations of each other:

3) List of recipes
-- /recipes/                           -> List of available recipes and variations
-- /recipes/ultimate-thing-with-sugar  -> The Ultimate Thing recipe with sugar
-- /recipes/ultimate-thing-with-honey  -> The Ultimate Thing recipe with honey



回答4:


I have a use case where the creation time of each version is crucial. So common questions that the API must answer are:

  1. What is the most recent version of /recipes/ultimate-thing?
  2. What version of /recipes/ultimate-thing was the most recent on 2019-01-01?
  3. What versions of /recipes/ultimate-thing were created between 2019-01-01 and and 2019-01-31?

Number 3. seems to me an obvious case for for query parameters:

-- /recipes/ultimate-thing?versions_since=2019-01-01&versions_until=2019-01-31

It will probably not hurt to use the same approach for 2.:

-- /recipes/ultimate-thing?version=2019-01-01

Then let's be consistent and use the same approach for 1.:

-- /recipes/ultimate-thing?version=latest

From this point of view your option 1) - using query parameters - seems most natural.



来源:https://stackoverflow.com/questions/15123451/restful-urls-for-distinct-versions-of-a-resource

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!