问题
Say I have two collection resources:
/persons
/organizations
A GET
to /persons/id/
returns a specific person. Likewise, a GET
to /organizations/id
returns a specific organization.
A person can be member of one or more organizations. In this relation context, we have data such as the role of the person in the organization, the date on which the person joined the organization, ...
Which of the designs make most sense?
A membership resource
/memberships/id
, to which aGET
returns the data of the relation context (together with a link to the person and the organization).A
/persons/id/organizations/id
and a/organizations/id/persons/id
. AGET
to one of the two returns the relation context, and aGET
to the other one redirects (http status code 303) to the other.Something else?
回答1:
Another option is to embed the relationships right into the resources themselves. This makes it easier for a client to follow relationships between resources as they consume the service. For example, here's a hypothetical person
with relationships to two organization
resources via two membership
resources, and one of those membership
resources:
"person890": {
"firstName": "Jane",
"lastName": "Smith",
"links": [{
"rel": "membership",
"href": "memberships/123"
}, {
"link": "membership",
"href": "memberships/456"
}]
}
"membership123": {
"role": "chairwoman",
"date: "12/23/2013",
"term": "3 years",
"links": [{
"rel": "person",
"href": "persons/890",
}, {
"rel": "organization",
"href": "organizations/7575"
}]
}
The basic principle at work here is HATEOAS - "Hypermedia as the Engine of Application State" - which enables a client with minimal understanding of your data to still interact with your API.
回答2:
If your question is limited to the structure, I think there's no objectively correct answer. In principle, you should stick with whatever keeps consistency across your API. If there's nothing like this already implemented, I think it depends on what your goal is. If you want to keep the API as simple as possible, option 1 seems good enough.
Usually, I try to make the API as flexible as possible for the clients, so that they can get the exact information they need with as few requests as possible, and without bothering me to implement custom endpoints. Assuming organizations can be huge and have a lot of members, while a person can't be a member of a lot of organizations, this is what I'd do:
-I see no reason to have the two-level URI on both sides, so /persons/id
can be the canonical URI for the person and /persons
to the paginated collection of all persons across all organizations. organizations/id
can be the URI for the organization, and /organizations/id/persons
can give you a collection to all persons within an organization, and an alternative URI for the person.
I see no need for the 303, but that's a matter of option. You may have
/organizations/id/persons/id
redirect to/persons/id
if you want.Keep the
/memberships/id
as you described in 1.Assuming you're using some form of HATEOAS, all resources should have links to the related resources.
A few other ideas I often implement that help usability and flexibility are:
All resources should have a self link to the canonical URI.
You should be able to query the collections. Like
/memberships?person_id=X
should generate a subset of the collection that lists all membership instances for that person.You should be able to expand a resource representation to include an embedded representation. It may be something explicit, like
/persons/id?expand=memberships
should generate a representation of person with a field containing an embedded list of all memberships, or you can use something I call the zoom protocol. You have a parameter that indicates how many levels of relationships should be embedded, decreasing it as you progress through the relationships. So,/persons/id?zoom=1
will embed memberships,/persons/id?zoom=2
will embed memberships, and apply zoom=1 to the membership representations themselves, embedding organizations.
来源:https://stackoverflow.com/questions/26429924/rest-relation-context