标签云

微信群

扫码加入我们

WeChat QR Code

什么是休息的最佳实践嵌套资源

As far as I can tell each individual resource should have only one canonical path. So in the following example what would good URL patterns be?

Take for an example a rest representation of Companies. In this hypothetical example each company owns 0 or more departments and each department owns 0 or more employees.

A department can't exist without an associated company.

An employee can't exist without an associated department.

Now I'd find the natural representation of the resource patterns to be.

  • /companies A collection of companies - Accepts put for a new company. Get for the entire collection.
  • /companies/{companyId} An individual company. Accepts GET, PUT and DELETE
  • /companies/{companyId}/departments Accepts POST for a new item. (Creates a department within the company.)
  • /companies/{companyId}/departments/{departmentId}/
  • /companies/{companyId}/departments/{departmentId}/employees
  • /companies/{companyId}/departments/{departmentId}/employees/{empId}

Given the constraints in each of the sections I feel that this makes sense if a bit deeply nested.

However my difficulty comes if I want to list (GET) all employees accross all companies.

The resource pattern for that would most closely map to /employees (The collection of all employees)

Does that mean that I should have /employees/{empId} also because if so then there are two URI's to get the same resource?

Or maybe the entire schema should be flattened but that would mean that employees are a nested top level object.

At a basic level /employees/?company={companyId}&department={deptId} returns the exact same view of employees as the most deeply nested pattern.

What's the best practice for URL patterns where resources are owned by other resources but should be query-able separately?

See my answer below to see what I've done.


This is almost exactly the oppsite problem to that described in stackoverflow.com/questions/7104578/… though the answers may be related. Both questions are about ownership but that example implies that the top level object isn't the owning one.

2018年05月27日13分19秒

Exactly what I was wondering about. For the given use case your solution seems fine, but what if the relation is an aggregation rather than a composition? Still struggling to figure out what the best practice is here... Also, does this solution imply only the creation of the relationship, e.g. an existing person is employed or does it create a person object?

2018年05月27日13分19秒

It creates a person in my fictitious example. The reason I used those domain terms is its a reasonably understandable example, though mimicking my actual problem. Have you looked through the linked question that may halp you more for an aggragation relationship.

2018年05月27日13分19秒

I've split my question into an answer and a question.

1970年01月01日00分03秒

I'm voting to close this question as off-topic because its about a pattern rather than a programming problem and has gathered too many votes.

1970年01月01日00分03秒

This is pointing out the spirit of RESTful, there are no rules that say you should or should not do if only you consider a meaningful resource first. But further, I wonder what's the best practice for not duplicating code in such scenarios.

2018年05月27日13分19秒

abookyun if you need both routes, then repeated controller code between them can be abstracted to service objects.

2018年05月27日13分19秒

This has nothing to do with REST. REST does not care about how you structure the path part of your URLs... all it cares about is valid, hopefully durable URIs...

2018年05月27日13分19秒

Driving at this answer, I think any api where the dynamic segments are all unique identifiers shouldn't need to handle multiple dynamic segments (/company/3/department/2/employees/1). If the api provides ways to get each resource, then making each of those requests could be done in either a client side library or as a one-off endpoint that reuses code.

2018年05月28日13分19秒

While there is no prohibition, I consider it more elegant to have only one path to a resource - keeps all mental models simpler. I also prefer that URIs don't change their resource type if there is any nesting. for example /company/* should only return the company resource and not change resource type at all. None of this is specified by REST - its generally a poorly specified - just personal preference.

2018年05月27日13分19秒

Was very refreshing to come across this answer. I have been using nested endpoints for several months now after being taught that was the "right way". I came to all of the same conclusions you listed above. So much easier with a non-nested design.

2018年05月27日13分19秒

Would it also mean that primary key should be delivered a parameter of a query string instead of being delivered as a part of main url. /Employees?id=500, instead of /Employees/500. Wouldn't that also scale better in case you also require a primary key from a parent resource?

2018年05月27日13分19秒

You seem to list some of the downsides as upsides. "Just cram more parameters into a single end-point" makes the API harder to document and learn, not the other way around. ;-)

2018年05月27日13分19秒

Not a fan of this answer. There's no need to introduce redundant endpoints just because you've added a nested resource. It's also not a problem to have the same resource returned by multiple parents, provided those parents genuinely own the nested resource. It's not a problem to get a parent resource to learn how to interact with the nested resources. A good discoverable REST API should do this.

2018年05月27日13分19秒

I have come up with the same type of design as well. I think it's intuitive to create things like this "where they belong", but then still be able to list them globally. Even more so when there's a relationship where a resource MUST have a parent. Then creating that resource globally does not make that obvious, but doing it in a sub-resource like this makes perfect sense.

2018年05月27日13分19秒

I guess you used POST meaning PUT, and otherwise.

2018年05月28日13分19秒

Actually no Note that I'm not using pre assigned Ids for creation as the server in this case is responsible for returning the id (in the link). Therefore writing POST is correct (can't do a get on the same implementation). The put however changes the entire resource but its still available at the same location so I PUT it. PUT vs POST is a different matter and is controversial too. For example stackoverflow.com/questions/630453/put-vs-post-in-rest

2018年05月28日13分19秒

Wes Even I prefer modifying verb methods to be under the parent. But, do you see passing query parameter for global resource is accepted well? Ex : POST /departments with query parameter company=company-id

2018年05月27日13分19秒

Ayyappa Its not something I would personally do, but I woudn't be too shocked if it did happen. Normally I don't like mixing query strings with posted bodies. But I have done it in the past many years ago.

2018年05月27日13分19秒

To me, this is an acceptable approach only if the nested object makes sense as an atomic object. If they are not, It wouldnt really make sense to break them apart.

2018年05月27日13分19秒

This is what I said, if you also want to be able to retrieve departments, meaning if you'll use a /departments endpoint.

2018年05月27日13分19秒

It may also make sense to allow departments to be included via lazy loading when fetching a company, eg GET /companies/{companyId}?include=departments, since this allows both the company and its departments to be fetched in a single HTTP request. Fractal does this really well.

2018年05月27日13分19秒

When you're setting up acls you probably want to restrict the /departments endpoint to only be accessible by an admin, and have each company access their own departments only through ` /companies/{companyId}/departments`

2018年05月27日13分19秒

MatthewDaly OData also does that nicely with $expand

2018年05月28日13分19秒

"How nice/understandable a URL is in a REST API is only interesting to you as the API developer, not the API client, as would the name of a variable in your code be." Why would this NOT be interesting? This is very important, if anyone but yourself is also using the API. This is part of the user experience, so I would say it is very important that this is easy to understand for the API client developers. Making things even more easy to understand by linking resources clearly is of course a bonus (level 3 in the url you provide). Everything should be intuitive and logical with clear relations.

2018年05月27日13分19秒

Joakim If you are making a level 3 rest API (Hypertext As The Engine Of Application State), then the url's path structure is absolutely of no interest to the client (as long as it is valid). If you are not aiming for level 3, then yes, it is important and should be guessable. But real REST is level 3. A good article: martinfowler.com/articles/richardsonMaturityModel.html

2018年05月27日13分19秒

I object to ever creating an API or UI that is not user friendly for human beings. Level 3 or not, I agree linking resources is a great idea. But to suggest doing so "makes it possible to change URL scheme" is to be out of touch with reality, and how people use APIs. So it's a bad recommendation. But sure in the best of all worlds everyone would be at Level 3 REST. I incorporate hyperlinks AND use a humanly understandable URL scheme. Level 3 does not exclude the former, and one SHOULD care in my opinion. Good article though :)

2018年05月27日13分19秒

One should of course care for the sake of maintainability and other concerns, I think you miss the point of my answer : the way the url looks does not deserve a lot of thinking and you should "just make a choice and stick to it/be consistent" as I said in the answer. And in the case of a REST API, at least my opinion, user friendlyness is not in the url, it is mostly in (the media type) Anyway I hope you understand my point :)

2018年05月27日13分19秒