I have been heavily invested in WCF for a long time now but the tide has mostly turned on public facing APIs (and some private), the industry has almost exclusively committed to the principles of RESTful APIs. WCF still has many advantages over REST but its biggest weakness, in my humble opinion, is the limited support for non-Microsoft platforms.

SOAP Service Proxy Generator Support

REST’s natural alignment to HTTP is what makes it so easy to use and implement on the widest variety of devices.

RESTful principles

Introduced by Roy Fielding (Dissertation on Network Based Software Architectures)

“The key principles or REST involve separating your API into logical resources. These resources are manipulated using HTTP requests where the method (GET, PUST, PUT, PATCH, DELETE) has specific meaning.”

REST should be thought of as a style rather than a standard, as with all styles how it is implemented can be considered a matter of taste, however, the following are considered the formal required constraints.

In order to describe how you would apply some of this RESTful design we will consider a fictitious and arbitrary request to  create an API for a library, where we would be support adding shelves and then add books to those shelves.

Designing Good Resource URIs

Lets begin with the naming conventions, because most .NET developers come from an object oriented background, it tends to ask us to look at each object as a self contained entity with methods treated as RPC  (either directly or via a Proxy). This tends to lead to method names with verbs that indicate some specific set of actions, the following would be bad examples of RESTful methods.

  • GetBookShelves
  • SetBookShelves
  • GetBook

With RESTful styling we should try to use pluralized nouns, our http method will tell us what we are actually doing with the URI.


api/bookshelves /// Gets a list of book shelves 
api/bookshelves/1 /// Gets a single book shelf based on the id of 1 
api/bookshelves/1/books /// Gets a list of books from the book shelf based on the id of 1
api/bookshelves/1/books/1 /// Gets a book from based on the id of 1, from the book shelf based on the id of 1

Specific calculations on collections or groups tend to separate the RESTful purists from the pragmatists,  for example, if we wanted to know how many pages exist on a shelf (not sure why you would) then that could defined by a property returned from by a simple GET.

api/bookshelves/1

Or I think, more appropriately, we create a new resource for example:

api/bookshelvespagetotals/1

I see the the following example a lot when perusing the web for API examples, the truth is this style diverges from RESTful principles but makes much more sense to me.

api/bookshelves/1/total

Responding to Resource Requests

Predictability is a really important for folks who are consuming your API, and while consistency on URIs helps discoverability I would suggest that consistent success/error handling helps developers deal with responses in a compatible manner. The basic consensus on Support HTTP Status Codes

  • 20x – Successful
  • 40x - The client did something wrong (formatting, authentication, authorization, etc.)
  • 50x - The server did something wrong, there is no obvious remedy from a client perspective.

Here are some more specific response I see used for GET, POST, DELETE, PUT and PATCH.

api/bookshelves

  • GET -> 200 (Ok); 404 (Not Found), 500 (Internal Server Error)
  • POST  -> 201 (Created); 400 (Bad Request), 500 (Internal Server Error)

api/bookshelves/1

  • GET -> 200 (Ok); 404 (Not Found), 500 (Internal Server Error)
  • DELETE-> 204 (No content); 404 (Not Found), 400 (Bad Request); 500 (Internal Server Error)
  • PUT -> 200 (Ok); 404 (Not Found), 400 (Bad Request); 500 (Internal Server Error)
  • PATCH -> 200 (Ok); 404 (Not Found), 400 (Bad Request); 500 (Internal Server Error)

In general the following also apply:

  • 401 (Unauthorized) – Invalid/No credentials supplied.
  • 403 (Forbidden) – Authenticated user does not have access to a specific resource.
  • 405 (Method not allowed) – A method is being requested that the authenticated user is not allowed to process.

Creating RESTful ASP.NET Web API

To create a new project in Visual Studio 2013 you can elect to create a new MVC project and then you are able select Web API.

VSWebAPI

This action does a few things in the background but for now I want to focus on the Controller, which if you will notice looks an awful lot like MVC. One of the differences of course is the use of System.Web.Http.ApiController vs System.Web.Mvc.Controller. I am told that in  ASP.NET version 5 we have been reduced to one controller, methods then return ActionResult by default.

public class BookShelvesController : System.Web.Http.ApiController
{
    // GET api/bookshelves
    public IEnumerable<string> Get()
    {
		return new string[] { "value1", "value2" };
    }

    // GET api/bookshelves/5
    public string Get(int id)
    {
		return "value";
	}

    // POST api/bookshelves
    public void Post([FromBody]string value) { }    
	
	// PUT api/bookshelves/5
    public void Put(int id, [FromBody]string value){  }

    // DELETE api/bookshelves/5
    public void Delete(int id){ }
}

As the comments clearly indicate the actions map to HTTP methods (GET, POST, PUT, DELETE). However by convention if you wanted a more descriptive name in your controller you could also prefix your action method name with HTTP verb for example “GetCustomer”. Additionally you could assign default HTTP verbs by adding attributes to any methods you choose:

[HttpGet]
[HttpPost]
[HttpPut]
[HttpDelete]

Configuring Routes

Routes are what map your URI request to an ApiController the default route looks like this:

api/{controller}/id

  • {controller} + “Controller” = ApiController type name.
  • {id} is passed as an argument to the action method.
  • HTTP Verb (Get, Post, Put, Delete) is used to determine action.

Using our example then…

api/bookshelves/26

  • “bookshelves” => BookShelvesController class.
  • HTTP GET implies method that starts with the word “Get” or a method with the attribute of HttpGet.
  • In this case “26” is passed to the Get(object id) method.

Your default routing code is found in App_Start/RouteConfig.cs

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
		routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
		
        routes.MapRoute(
			name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
	}
}

We can use routing to map multiple URIs to the same set of resource, for example by implementing some basic changes to Routing you can ensure that a default to call like this:

api/books/1 

Can have equivalency to the following:

api/bookshelves/1/books/1


Comment Section

Comments are closed.