Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Building an ASP.NET Web API RESTful Service – Part 9

4.73/5 (5 votes)
5 Mar 2014CPOL3 min read 23.8K  
This is the ninth part of Building ASP.NET Web API RESTful Service Series.

This is the ninth part of Building ASP.NET Web API RESTful Service Series. The topics we’ll cover are:

Update (2014-March-5) Two new posts which cover ASP.Net Web API 2 new features:

Preparing Web API for Versioning

Once you publish your successful API, consumers will start depending on it and your exposed data will be used and integrated with other data in different ways, but it is known that change is inevitable and new requirements and changes will evolve, if you are lucky you will be able to adopt those changes without introducing any breaking changes for existing clients, but this is not the case always.

So our API needs to change and we have to start building different versions from our API to handle those changes without affecting existing consumers. We need to run the new version concurrently with older version of the API to give consumers enough time to migrate to the latest version, in some cases multiple versions of the API can live forever side by side.

There are multiple ways to version your API, each way has it is own pros and cons, in this post and the coming one we’ll cover versioning by URI, query string, custom header, and by accept header.

Introducing a change and preparing our API

In our case and for keeping things simple we’ll introduce a breaking change on GET method in the “StudentsController” so we’ll return “FullName” and “CoursesDuration” in response body instead of “FirstName” and “LastName” properties.

The simplest way I found in versioning your API is to create an identical controller of “StudentsController” and name it “StudentsV2Controller”, notice the suffix V2 in controller name, we’ll depend on this part to select the appropriate controller based on API version. Now in this controller we’ll introduce our breaking changes on GET method and we keep other HTTP verbs the same as we didn’t introduce any change on them.

Special thanks goes to Shawn Wildermuth for describing this simple yet effective technique for API versioning in his plural sight course.

Currently the JSON response for GET method in “StudentsController” as below:

JavaScript
[
    {
        "id": 2,
        "url": "http://localhost:8323/api/students/HasanAhmad",
        "firstName": "Hasan",
        "lastName": "Ahmad",
        "gender": 0,
        "enrollmentsCount": 4
    },
    {
        "id": 3,
        "url": "http://localhost:8323/api/students/MoatasemAhmad",
        "firstName": "Moatasem",
        "lastName": "Ahmad",
        "gender": 0,
        "enrollmentsCount": 4
    }
]

Now we want to achieve the below JSON response when calling the GET method in our new “StudentsV2Controller”:

JavaScript
[
    {
        "id": 2,
        "url": "http://localhost:8323/api/students/HasanAhmad",
        "fullName": "Hasan Ahmad",
        "gender": 0,
        "enrollmentsCount": 4,
        "coursesDuration": 13
    },
    {
        "id": 3,
        "url": "http://localhost:8323/api/students/MoatasemAhmad",
        "fullName": "Moatasem Ahmad",
        "gender": 0,
        "enrollmentsCount": 4,
        "coursesDuration": 16
    }
]

Now let’s copy and paste the existing controller “StudnetsController” and rename the new one to “StudentsV2Controller”, we need to change the GET implementation as the code below:

C#
public IEnumerable<StudentV2BaseModel> Get(int page = 0, int pageSize = 10)
{
    IQueryable<Student> query;

    query = TheRepository.GetAllStudentsWithEnrollments().OrderBy(c => c.LastName);

    var totalCount = query.Count();
    var totalPages = Math.Ceiling((double)totalCount / pageSize);

    var urlHelper = new UrlHelper(Request);
    var prevLink = page > 0 ? urlHelper.Link("Students", new { page = page - 1, pageSize = pageSize }) : "";
    var nextLink = page < totalPages - 1 ? 
      urlHelper.Link("Students", new { page = page + 1, pageSize = pageSize }) : "";

    var paginationHeader = new
         {
             TotalCount = totalCount,
             TotalPages = totalPages,
             PrevPageLink = prevLink,
             NextPageLink = nextLink
         };

    System.Web.HttpContext.Current.Response.Headers.Add("X-Pagination",
                                                         Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));

    var results = query
                .Skip(pageSize * page)
                .Take(pageSize)
                .ToList()
                .Select(s => TheModelFactory.CreateV2Summary(s));

    return results;
}

The code above is almost the same as the GET method in “StudentsController”, the only change we did is returning a new response type “StudentV2BaseModel” by calling the new method “CreateV2Summary” in the “ModelFactory” class. The code below lists the new Data Model “StudentV2BaseModel” and the new function “CreateV2Summary”. Note that V2 suffix in the model name and function name are not mandatory to implement versioning, you can name those anything you want.

C#
public class StudentV2BaseModel
{
    public int Id { get; set; }
    public string Url { get; set; }
    public string FullName { get; set; }
    public Data.Enums.Gender Gender { get; set; }
    public int EnrollmentsCount { get; set; }
    public double CoursesDuration { get; set; }
}

public class ModelFactory
{
    public StudentV2BaseModel CreateV2Summary(Student student)
    {
        return new StudentV2BaseModel()
        {
            Url = _UrlHelper.Link("Students", new { userName = student.UserName }),
            Id = student.Id,
            FullName = string.Format("{0} {1}", student.FirstName, student.LastName),
            Gender = student.Gender,
            EnrollmentsCount = student.Enrollments.Count(),
            CoursesDuration = Math.Round(student.Enrollments.Sum(c=> c.Course.Duration))
        };
    }
}

Now we have prepared our “Students” resource to be versioned, in the next post we will start implementing different techniques for versioning.

Source code is available on GitHub.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)