0

I'm developing an ASP.NET Web Api 2 RESTful web api with .NET Framework 4.5.1 and C# and I'm trying to understand how to expose resources through a Web Api:

Imagine that I'm a group owner and I am the only one that can add new users to that group (I'm trying to simulate a WhatsApp group).

This is my C# class for user:

public class User
{
    public int UserId { get; set; }
    public string UserName { get; set; }

    public List<Group> Groups { get; set; }
    public List<Group> GroupsOwned { get; set; }
}

And Group class:

public class Group
{
    public int GroupId { get; set; }
    public string GroupName { get; set; }

    public User Owner { get; set; }

    public List<User> Members { get; set; }
}

How do I have to expose user's groups owned members in a RESTful Web Api?

I think I have to expose it this way:

"api/users/{ownerId}/groupsOwned/{groupId}/members/"

I will allow PUT to modify the Group to add a new member, and probably GET to get all members.

Any advice?

  • Why do you want to add the owner (which more seems like an attribute of a group) into the url? It can make sense but I don't see an argument for it. /api/groups/{groupId}/members seems to make more sense? Then you can add another path: /api/users/1/groups and /api/users/1/groupsOwned where you show a collection of links to the normal groups urls. – Luc Franken Sep 9 '14 at 12:52
  • Because I need to pass the ID for the user that is modifying the group. If the user is the owner, he can add more members to the group. – VansFannel Sep 9 '14 at 13:07
  • 1
    In general that sounds as security and should be fixed by headers (like basic Auth, OAuth etcetera). It does not change which resource you show. Even better: When the owner of a group changes all urls would be invalid which is not restful. So I would suggest to leave them away and make it an attribute. – Luc Franken Sep 9 '14 at 13:35
  • @LucFranken I think that your last comment is the answer for my question. Thanks. – VansFannel Sep 9 '14 at 13:44
1

Why do you want to add the owner (which more seems like an attribute of a group) into the url? It can make sense but I don't see an argument for it. /api/groups/{groupId}/members seems to make more sense? Then you can add another path: /api/users/1/groups and /api/users/1/groupsOwned where you show a collection of links to the normal groups urls.

Because I need to pass the ID for the user that is modifying the group. If the user is the owner, he can add more members to the group.

In general that sounds as security and should be fixed by headers (like basic Auth, OAuth etcetera). It does not change which resource you show. Even better: When the owner of a group changes all urls would be invalid which is not restful. So I would suggest to leave them away and make it an attribute.

Interesting to read when you try to work restful is the concept the author of it has: Roy Fielding is the inventor of the REST concept and you can find some basics about the real concept here:

http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

2

From your object-model I conclude, that you have at least two distinct ressources:

  • users
  • groups

So there are two base-URLs:

(1)/api/users/ and (2)/api/groups/

(1) offers:

/api/users/ GET show all users available Response: the representation of all users

/api/users/?group=someGroup GET show all users which are members of someGroup available Response: the representation of all users of the group

/api/users/{id} GET to show individual user Response: the representation of one user

/api/users/{id}/groups GET to show individual user's groups Response: the representation of one user's groups

/api/users/ POST to create a new user Response: the URI of the new user

/api/users/{id} PUT/PATCH to update an existing user Response: No content

/api/users/{id} DELETE to delete an individual user Response: No content

(2) offers:

/api/groups/ GET show all groups available Response: the representation of all groups

/api/groups/{id} GET to show individual group Response: the representation of one group

/api/groups/{id}/members GET to show individual group's members Response: the representation of one group's member

/api/groups/ POST to create a new group Response: the URI of the new group

/api/groups/{id} PUT/PATCH to update an existing group Response: No content

/api/groups/{id} DELETE to delete an individual group Response: No content

Usually you respond to every request, if not otherwise advised, with 200 OK.

Imagine that I'm a group owner and I am the only one that can add new users to that group

This doesn't matter for your API. That a user is allowed to do something or not is a secondary problem. If you want to add a user to a group, your API could provide either one (or both) of the two ways

  • /api/groups/{id}/members PUT/PATCH Add group members
  • /api/users/{id} PUT/PATCH update single user

If a user isn't allowed to do the chosen action, the server should respond with 403 Forbidden

Because I need to pass the ID for the user that is modifying the group. If the user is the owner, he can add more members to the group.

Since the user is logged in, you have his ID. You could easily save this along with the updated information.

  • Thanks for your answer. I'm thinking about how to add a user to a new group, and I prefer to do it with /api/groups/{id}/members. But I need to expose /api/users/{id} to update an user. Is it correct if I disable user's group update in /api/users/{id}? Maybe someone that uses my api expects to update an user group with the last URI. – VansFannel Sep 10 '14 at 6:48
  • Hm. That's difficult to answer. In general it is your API and it is up to you, how you design ist. The intuitive way would be /api/groups/{id}/members. So, disabling would be no greater problem. On the other hand - if you expose the groups via the user-object I would allow both for consistency reasons. – Thomas Junk Sep 10 '14 at 7:16
0

When you say 'expose user's groups owned members in a RESTful Web Api?' This could mean two things:

  • URI structure (you seem to have identified this already)
  • Resource representation (html, json etc)

If you are talking about representation, my personal preference is JSON HAL. It allows a really simple model of _links, _data, _embedded which helps drive descoverability and connectedness.

From your comments you seem to want to know how to access the members. This is simply

api/users/{ownerId}/groupsOwned/{groupId}/members/{memberId}

Or if you want to access all members:

api/users/{ownerId}/groupsOwned/{groupId}/members/

But be careful, you should really use nested representations of resource when you return a collection of resources like the above URI would produce. Look at the HAL spec above, that will help explain embedding resources into resources.

EDIT

I think I have to expose it this way:

"api/users/{ownerId}/groupsOwned/{groupId}/members/" I will allow POST to add new members, and probably GET to get all members.

Yes, that's correct

  • updated answer above – Gaz_Edge Sep 9 '14 at 11:57
0

When constructing a REST service, I try to focus on the object in question and use the JSON to represent the structure of the data.

In this example, the object to be modified is the group. I would put this at the base of the URI and not have it buried in the middle.

/api/groups/{groupId}

This should be valid because (hopefully) group IDs are unique across the application.

Next is the question of how to update members.

I imagine a JSON which looks something like:

{
    "groupId": "1111",
    "groupName": "A Group",
    "ownerId": "2222",
    "membersIds": [ "2222", "3333", "4444" ]
}

The first option would be to PUT the whole object.

PUT /api/groups/1111

{
    "groupId": "1111",
    "groupName": "A Group",
    "ownerId": "2222",
    "membersIds": [ "2222", "3333", "4444" ]
}

This is a simple API. A client would use a GET to get the current data, make changes, then PUT the result. It would be up to the server to validate the update as a whole, or reject the update as a whole if there is some error.

I understand this may not always be desirable, so I could understand wanting a shortcut which would only update members.

To this end here is the members shortcut:

/api/groups/{groupId}/members

with the corresponding JSON:

[ "2222", "3333", "4444" ]

This would still be a PUT because you are updating the existing data.

PUT /api/groups/1111/members

[ "2222", "3333", "4444" ]

The PUT would replace all the current members with the members in the array. The current members could be requested with GET /api/groups/1111/members. It would be the client's responsibility to add new members to the current members.

This is still not what you asked for. You want a shortcut to simply add members. Here my method breaks down. You are not using the URI to represent the structure of the data, you are using the URI to perform an action with the data. This is not very RESTful (IMHO), but lots of people do it.

In the past, I've use CGI parameters to explicitly show an action.

/api/groups/{groupId}?action=addMembers

Finally, we get to something that looks like what you asked for.

POST /api/groups/1111?action=addMembers

[ "3333", "4444"]

To me, it doesn't look very RESTful, but utility trumps ideology and if this is exactly what you need, then here it is.

  • Thanks for your answer. I need to pass the ID of the user that is updating the group, this is why I've used: api/users/{ownerId}. – VansFannel Sep 9 '14 at 13:30
  • @VansFannel Why? You should rely on your authentication mechanism to tell you which user is making changes. If you need to masquerade the authenticated user making changes as another user, this should be much more explicit (think sudo). – Jeffery Thomas Sep 9 '14 at 20:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.