Accept header based web API action selection

Introduction to the queue for the queue ...

I have a resource defined in

http://my-awesome-product.com/api/widgets/3 

which is a widget with an id of 3. In the Web API, I would define a controller to serve this resource as follows:

 public class WidgetsController : ApiController { public Widget Get(int id) { return new Widget(...); } } 

Now the Widget class can potentially be quite large, and it wants to save bandwidth from the database to the web server and from the web server to the client, I create several DTO classes that contain a limited number of fields from the total amount of Widget . For instance:

 public class WidgetSummary { public int Id { get; set; } public string Code { get; set; } public string Description { get; set; } public decimal Price { get; set; } } public class FullWidgetForEditing { public int Id { get; set; } public string Code { get; set; } public string Description { get; set; } public decimal Weight { get; set; } public decimal Price { get; set; } public Color Color { get; set; } public decimal Width { get; set; } public decimal Height { get; set; } public decimal Depth { get; set; } } public class WidgetForDropDownList { public int Id { get; set; } public string Code { get; set; } } 

With these views, the Widget client requests WidgetSummary to display the entire Widget in the system grid, it requests FullWidgetForEditing on the edit page of a specific Widget , and it requests WidgetForDropDownList for use in the drop-down list on the order form.

From what I understand in REST, there must be one URL to access Widget , since it is the only resource, regardless of its presentation, and the client must specify Accept headers with a media type parameter to extract Widget in different forms, for example my-awesome-product.type=widgetsummary , my-awesome-product.type=fullwidgetforediting and my-awesome-product.type=widgetfordropdownlist .

I could achieve this in the Web API by checking the request headers like this:

 public class WidgetsController : ApiController { public object Get(int id) { if (Request.Headers.Accept.Any(x => x.Parameters.Any(y => y.Name == "my-awesome-product.type" && y.Value == "widgetsummary"))) { return new WidgetSummary(...); } else if (Request.Headers.Accept.Any(x => x.Parameters.Any(y => y.Name == "my-awesome-product.type" && y.Value == "fullwidgetforediting"))) { return new FullWidgetForEditing(...); } else if (Request.Headers.Accept.Any(x => x.Parameters.Any(y => y.Name == "my-awesome-product.type" && y.Value == "widgetfordropdownlist"))) { return new WidgetForDropDownList(...); } throw new HttpResponseException(HttpStatusCode.NotAcceptable); } } 

However, this is quickly becoming erratic as the number of types grows and makes unit testing difficult. I would really like to do the following:

 public class WidgetsController : ApiController { [HttpGet(MediaType = "my-awesome-product.type", MediaTypeValue = "widgetsummary")] public WidgetSummary GetWidgetSummary(int id) { return new WidgetSummary(); } [HttpGet(MediaType = "my-awesome-product.type", MediaTypeValue = "fullwidgetforediting")] public FullWidgetForEditing GetFullWidgetForEditing(int id) { return new FullWidgetForEditing(); } [HttpGet(MediaType = "my-awesome-product.type", MediaTypeValue = "widgetfordropdownlist")] public WidgetForDropDownList GetWidgetForDropDownList(int id) { return new WidgetForDropDownList(); } } 

And you have an API for a specific action method based on a media type. I explored the ApiControllerActionSelector override, however I started pulling out a lot of the existing code for this attribute because I really need the default action selection, but in case of ambiguous actions, I want to filter the list of actions based on the media type. I really want this to be non-intrusive, so that I can mix convention routing, standard attribute routing (which is in Web API v2) and this hypothetical attribute routing in the same controller, if necessary.

A question of time: is it possible to implement such a routing strategy using the Web API in its current form? Do I have to completely override ApiControllerActionSelector to achieve this (and then make sure my reevaluation remains up to date)?

+6
source share
1 answer

So, here is the question you need to think about. Are these really three views or three different resources? If you decide to cache these views, will the URI be sufficient to identify the cached view, or will you also need to change the accept header?

If you agree that they are different enough to be resources, they must be identified using different URLs. If you decide to use hyperlinks in your answers, would you like to point to "widgetsummary" regardless of "widgeteditform"?

Usually, multiple views are required if you want to support different client devices that can only support certain types of media. for example, Device A receives Rep1, and Device B receives Rep2. It’s rare that you want device A to access both devices A and Rep1 and Rep2.

Just my thoughts ...

+2
source

Source: https://habr.com/ru/post/952485/


All Articles