Search code examples
c#.netrestasp.net-web-apirestful-url

Restful urls. View model specific


I've been going through this blog and I understand what he is saying, especially regarding the hierarchical structure (walking back along the path).

So

/objects/1/property

Removing property should give you the object with id 1, and removing the id should give you all the objects. Excellent and logical.

But I always expose my data via view models, so,

/objects/list/1 will give me the object list view model of the object with id 1. Or /objects/detail/1 will give me the object detail view model for the object with id 1.

Using this approach I have ended up with a long structure just to get a specific view model! I.e. objects/visualization/analysis/thread. Is this even restful? What I seem to be doing (subconsciously!) is structuring my restful API to match the namespace or module for where this specific view model lives (so in .NET it will be namespace: app.models.object.visualization.analysis).

How best to structure a restful endpoint like this? Is it better to have something like

objects-list/1 and objects-detail/1?

Thanks.

Example:

Sorry, I should have been more clear. I will give a .NET example. Suppose I have a cart class

public class Cart
{
    public int CardId { get; set; }

    public string CartName { get; set; }

    public DateTime Created { get; set; }

    public DateTime LastUpdated { get; set; }

    public IEnumerable<CartItem> Items { get; set; }
}

With a restful design, I could expose carts as /carts, /carts/1, /carts/1/items and so on. But I always expose view models, not the actual data layer object. I.e.

public class CartListModel
{
    public int CartId { get; set; }
    public string CartName { get; set; }
}

and

public class CartViewModel
{
    public int CartId { get; set; }
    public string CartName { get; set; }
    public DateTime LastUpdated { get; set; }
    public IEnumerable<CartItemViewModel> Items { get; set; }
}

So this way I am only exposing the data that I actually need for a specific purpose. Now at the moment, I am exposing these view models as such /carts/list or /carts/list/1. Also /carts/view and /carts/view/1. So the original question is this restful? Do I infact need a separate endpoint for each view model? So /carts-list and /carts-view, carts-view/1 etc.

Non .NET example

Don't really know what to put here! A view model is a representation of the object, only exposing certain properties necessary to bind to a view.

So suppose my object has the following JSON structure

{
    id: 1,
    name: 'Cart 1',
    lastUpdated: '26-Sep-2014 16:51:23',
    items: [
       // an array of objects
    ]
}

For a certain view, like a simple table, I may only need the id and the name. So I expose a restful endpoint that gives me back the following structure

{
    id: 1,
    name: 'Cart 1'
}

Everything else is unnecessary. For a cart edit page, I will probably need a lot more data than just the id and name. The question is, how do I structure a restful endpoint to expose these different representations of the same object?


Solution

  • URIs are stable

    Resources are identified by URIs. The get the object with ID 1, do

    GET /objects/1
    

    To get a list of all objects, just

    GET /objects
    

    Use content negotiation

    What representation of the object 1 is returned by the server is decided by content negotiation. This is done using HTTP headers, not URL path segments or query parameters. Do this:

    GET /objects/1
    Accept: appliction/vnd.com.example.object.detail+json
    

    By this the client could request something you call the "detail view model".

    If the client wants to get the "list view model", you could do

    GET /objects/1
    Accept: appliction/vnd.com.example.object.list+json
    

    Note

    • The URL is the same for both requests.
    • The Accept headers have different values.

    Don't use different URIs

    Don't do any of these:

    • GET /objects/1/list: This would request the sub resource called list from object 1.
    • GET /objects/1/list: This would request another sub resource.
    • GET /objects/1?model=detail or GET /objects/1?model=list: These are different URIs which identify different resources.