Rules to Better WebAPI

Since 1990, SSW has supported the developer community by publishing all our best practices and rules for everyone to see. 

If you still need help, visit SSW Consulting Services ​and book in a consultant.​​​

Hold on a second! How would you like to view this content?
Just the title! A brief blurb! Gimme everything!
  1. Do you provide versioning?

    As an API provider, one of your most important tasks is to make sure that breaking changes will never occur in your API. Making breaking changes will make life difficult for the developers who depend on your service and can easily start causing frustration when things start to break.

    There are typically three main ways people provide versioning.

    1. Change the URL: Append a version number in the path
      e.g. https://developer.github.com/v3/
      1. Best choice for anonymous API access (callers may not be authenticated)
      2. E.g. Github, Twitter​
    2. Based upon caller: The caller has been authenticated and you determine the version of the API based upon the customer's record.
      1. Your URL's never change
      2. Allows you to see the oldest version that customers are using and notifying customers to upgrade
      3. E.g. Salesforce
    3. Custom request header: You can use the same URL but add a header such as "api-version: 2"
      1. Your URL's never change

    All of these methods work well. The above list is in order of our recommendations. ​

    Option 2 is a viable solution as you only have a few authenticated users that will consume the web service. It also allows you to notify users if they are using an old version that will be decommissioned.

    If you are working with objects, keep the object id in the URL and leave everything else to the query string.

    Read more about Do you provide versioning?
  2. Do you add timestamps?

    Passing date times is good. Just as a note that it is vital that you send any date/times with time zone information and at UTC.

    Figure: Include date and time information
    Read more about Do you add timestamps?
  3. Do You Apply the ValidateModel Attribute to All Controllers?

    Web API does not automatically return an error to the client when validation fails. It is up to the controller action to check the model state and respond appropriately.

    ​You can also create an action filter to check the model state before the controller action is invoked.​

    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;
    using System.Web.Http.ModelBinding;

    namespace MyApi.Filters
    {
    public class ValidateModelAttribute : ActionFilterAttribute
    {
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
    if (actionContext.ModelState.IsValid == false)
    {
    actionContext.Response = actionContext.Request.CreateErrorResponse(
    HttpStatusCode.BadRequest, actionContext.ModelState);
    }
    }
    }
    }​

    Figure: If model validation fails, this filter returns an HTTP response that contains the validation errors. In that case, the controller action is not invoked

    public class ProductsController : ApiController
    {
    [ValidateModel]
    public HttpResponseMessage Post(Product product)
    {
    // ...
    }
    }

    Figure: Bad Example - Set the filter as an attribute on individual controllers or controller actions

    public static class WebApiConfig
    {
    public static void Register(HttpConfiguration config)
    {
    config.Filters.Add(new ValidateModelAttribute());

    // ...
    }
    }

    Figure: Good Example - Add an instance of the filter to the ​HttpConfiguration.Filterscollection to apply this filter to all Web API controllers
  4. Do you document your WebAPI

    Documenting your WebAPI is important but you don't want to spend a massive amount of time trying to keep documentation up to date. The solution ​is to use Swagger and Swashbuckle

    ​​​​​Implementing Swashbuckle with give you an automatic UI to explore and test your WebAPI methods.

    swagger.png

    ​Swagger gives you a nice UI automatically generated on top of your WebAPI methods



    Read more about Do you document your WebAPI
  5. Do you know how to easily get classes from a JSON response?

    ​​When integrating with external Web APIs which return a JSON response, there is a quick and easy way to generate classes to handle that response.


    Execute the request, and copy the text of the JSON response.

    8-08-2014-3-41-23-PM-compressor.png

    Create a new class in Visual Studio, and Click Edit | Paste Special | Past As JSON Classes and classes will be generated from the JSON in the clipboard.

    8-08-2014-3-53-17-PM-compressor.png
    Figure: Edit | Paste Special | Paste JSON As Classes

    8-08-2014-3-56-34-PM-compressor.png
    Figure: Classes generated from the JSON

    The results may need cleaning up a little bit, but its much easier than trying to write them manually.


  6. Do you know that WebAPI and tables name should be consistent?

    ​When creating WebAPIs for your applications, it is useful to keep the naming consistent all the way from the back-end to the front-end.

    Table name: Employees
    Endpoint: /api/Users

    ​Bad Example: The endpoint is different to the table name

    ​Table name: Employees
    Endpoint: /api/Employees

    ​Good Example: Table name is the same as the WebAPI endpoint


    By making the endpoint the same as the table name, you can simplify development and maintenance of the WebAPI layer.

    In some circumstances you may not have direct control over the database however. In these situations it may make sense to have different endpoint names if doing so will simplify development for consumers of your WebAPI endpoints.​

  7. Do you know the best way the best way to generate your entities from swagger?

    To generate entities from a swagger rest definition a tool can automate this.

    There are 3 ways:

    1. AutoR​esthttps://github.com/Azure/aut​orest
    2. NSwagStudiohttps://github.com/NSwag/NSwag/wiki/NSwagStudio
    3. Swagger Codegen - http://swagger.io/swagger-codegen​
    nswagstudio-search.jpg
    Figure: Bad example - only around 1,​000 results​
    swagger-codegen-search.jpg
    Figure: OK example - Around 40,000 results
    autorest-search.jpg
    Figure: Good example - 130,000+ results
  8. Do you return a Resource URL?

    When the Web API creates a resource, it should include the URI of the new resource in the Location header of the response.​

    public Product PostProduct(Product item)
    {
    item = repository.Add(item);
    return item;
    } ​

    Figure: Bad Example – The response does not contain a reference to the location of the new resource

    public HttpResponseMessage PostProduct(Product item)
    {
    item = repository.Add(item);
    var response = Request.CreateResponse(HttpStatusCode.Created, item);

    string uri = Url.Link("DefaultApi", new { id = item.Id });
    response.Headers.Location = new Uri(uri);
    return response;
    }

    Figure: Good Example – The response message contains a link in the header to the created resource (and the “Created” status code is returned )​
  9. Do you return detailed error messages?

    Good error design is as important to the sucess of an API as the API design itself. A good error message provides context and visibility on how to troubleshoot and resolve issues at critical times.

    Start by using the correct HTTP Status Codes.

    The HTTP/1.1 RFC lists over 70 different HTTP Status Codes. Very few developers will be able to remember all of them, so it pays to keep it simple and use the most common Status Codes. The basic rule is to use the following three:

    • 200 OK - Everything worked. Success
    • 400 Bad Request - The consuming application did something wrong.
    • 500 Internal Server Error - The API Application did something wrong.

    Make your error messages as verbose as possible.

    Error messages should contain a sufficient level of information that a developer or consuming client can act upon.

    {
        "errorMessage": "An error has occurred."
    }

    Figure: Bad Example - The error message does not contain information that can be acted upon.

    {
        "errorMessage": "Client ID is a required field. Please provide a Client ID."
    }

    Figure: Good Example - The error message provides explicit detail and a short description on how to fix the issue.

    Provide a Tracking or Correlation ID

    A tracking or correlation ID will allows the consuming clients to provide the API developers with a reference point in their logs.

    {
        "errorMessage": "An error has occurred. Please contact technical support"
    }

    Figure: Bad Example - No tracking or correlation ID is provided.

    {
        "errorMessage": "An error has occurred. Please contact technical support",
        "errorId": "3022af02-482e-4c06-885a-81d811ce9b34"
    }

    Figure: Good Exmaple - A error ID is provided as part of the reponse.

    Provide an additional Help Resource

    Providing a URI to an additional help resources as part of your request will allow consuming clients to find additional resources or documentation that relates to the defined problem. 

    {
      "ErrorType": "DoesNotExist",
      "Id": "3022af02-482e-4c06-885a-81d811ce9b34",
      "Message": "No Client with a ID of 999999999 was found",
      "StatusCode": 404
    }

    Figure: Bad Example - No Help Link Provided

    {
      "ErrorType": "DoesNotExist",
      "HelpLink": "http://www.myapiapplication/api/help/doesnotexist",
      "Id": "3022af02-482e-4c06-885a-81d811ce9b34",
      "Message": "No Client with a ID of 999999999 was found",
      "StatusCode": 404
    }

    Figure: Good Example - A help link is provided as part of the response.

  10. Do you return the correct response code?

    The use of correct response codes is a simple yet crucial step, towards building a better WebAPI. By default, the WebAPI framework sets the response status code to 200 (OK), regardless if the task succeed or an error occurred.  

    You can save yourself countless hours of painful debugging, by specifying the correct response code.

    For example: According to the HTTP/1.1 protocol, when a POST request results in the creation of a resource, the server should reply with status 201 (Created).

    public Product PostProduct(Product item)
    {
    item = repository.Add(item);
    return item;
    }

    Figure: Bad Example – By default a 200 status code is returned.

    [ResponseType(typeof(CreditSnapshot))]
    public HttpResponseMessage PostProduct(Product item)
    {
    item = repository.Add(item);
    var response = Request.CreateResponse(HttpStatusCode.Created, item);

    return response;
    }

    Figure: Good Example – When creating objects the “Created” status code is returned. 

    ​public void PutProduct(int id, Product product)
    {
    product.Id = id;
    if (!repository.Update(product))
    {
    return Request.CreateResponse(HttpStatusCode.NotFound, ex.Message);
    }
    }

    Figure: Good Example – When updating or deleting objects, if the object to be modified cannot be found throw exception with HttpStatusCode.NotFound