Vlad's blog

In programming veritas

Posts Tagged ‘Web API

Web API versioning in real world applications

leave a comment »

Once you have published your API it is set in stone. Customers who use your API expect it to not change. Otherwise their code may fail due to changed contract or behavior. But requirements will change and we need to figure out a way to evolve API without breaking existing clients. So every time when a breaking API change occurs you need to release a new version of API. That does not mean you need to support all versions forever. But you need to get rid of old versions of API with some care so customers have an idea how to move to a newer version of API.
In this post I am going to discuss how Web API versioning affects entire application and provide some recommendations how to organize source code to maintain different versions of API.

Example project can be downloaded here.

Introducing test application

In order to illustrate API versioning strategies we are going to develop a simple Web API for retrieving tasks. A task contains Name, Description, Owner and task state attributes: IsStarted and IsFinished. Each task belongs to particular Project which is barely a container of tasks. Application consists of three layers:

  • Domain layer that includes Task and Project definition
  • Data access layer that defines TaskRepository class. TaskRepository uses domain classes in serialization.
  • Distributes services layer defines DTO’s used by TaskController

versioning-domain

Read the rest of this entry »

Written by vsukhachev

December 12, 2016 at 10:37 am

Posted in Development

Tagged with

Web API data shaping

leave a comment »

Data shaping is a simple technic for minimizing traffic travelling from Client of Web API to Server and vice versa. Client can send many requests to the Server for data retrieval and update. Depending on a scenario Client might not be interested in receiving all fields that comprise data object. For instance Client might want to receive only id and description and ignore other ten fields. When we deal with hundreds of objects travelling from Server to Client that can significantly affect application performance.

Optimizing data retrieval (HTTP GET)

Imagine we are developing simple application for managing tasks. Below is task class definition.

public class Task
{
 public int Id { get; set; }
 public string Summary { get; set; }
 public string Description { get; set; }
 public DateTime LastModified { get; set; }
 public DateTime Created { get; set; }
 public string UserCreated { get; set; }
 public string Assignee { get; set; }
 public TaskState State { get; set; }

 public enum TaskState
 {
   NotStarted,
   InProgress,
   Closed
 }
}

API should expose a method for retrieving task by id. So Url should look like this:

http://taskmanager.com/tasks/1

When this request is processed the Server returns the following result.

{
 "id": 1,
 "summary": "Test task summary",
 "description": "Test task description",
 "lastModified": "2015-11-04T08:29:17.5464689-05:00",
 "created": "2015-11-04T08:29:17.5464689-05:00",
 "userCreated": "UserCreated",
 "assignee": "Assignee",
 "state": 1
}

Consider scenario when Client wishes to receive only id plus summary. Ideally Url for achieving this goal should look like below.

http://taskmanager.com/api/tasks/1?fields=id,summary

Server side code is shown below. Optional argument fields is used to specify comma separated list of fields.

[RoutePrefix("api")]
 public class TaskController : ApiController
 {
   private ITaskRepository taskRepository = new TaskRepository();

   [Route("tasks/{taskId}")]
   public IHttpActionResult Get(int taskId, string fields = null)
   {
     try
     {
       var fieldsList = new List<string>();

       if (fields != null)
       {
         fieldsList = fields.ToLower().Split(',').ToList();
       }

       Task task = taskRepository.Find(taskId);

       if (task == null)
       {
         return NotFound();
       }

       object result = GetShapedObject(task, fieldsList);

       return Ok(result);
     }
     catch (Exception)
     {
       return InternalServerError();
     }
   }

Actual filtering is implemented in GetShapedObject method which uses a bit of reflection to create output data object that contains only fields requested by Client.

private static object GetShapedObject(Task task, List<string> fieldsList)
{
   if (fieldsList.Count == 0)
   {
     return task;
   }

   ExpandoObject result = new ExpandoObject();

   foreach (var field in fieldsList)
   {
     var fieldValue = task.GetType()
     .GetProperty(field, 
        BindingFlags.IgnoreCase | 
        BindingFlags.Public | 
        BindingFlags.Instance)
     .GetValue(task, null);

     ((IDictionary<String, Object>)result).Add(field, fieldValue);
   }

   return result;
}

So the following Url will return only id and summary.

http://taskmanager.com/api/tasks/1?fields=id,summary

{
 "id": 1,
 "summary": "Test task summary"
}

Optimizing data update (HTTP PATCH)

In HTTP world PUT is used for updating a resource. By definition Client should specify all fields of data object, otherwise they will be reset to defaults. I.e. if you want to change only summary you should send entire Task object in a body of PUT request.

{
 "id": 1,
 "summary": "New task summary",
 "description": "Test task description",
 "lastModified": "2015-11-04T08:29:17.5464689-05:00",
 "created": "2015-11-04T08:29:17.5464689-05:00",
 "userCreated": "UserCreated",
 "assignee": "Assignee",
 "state": 1
}

Sometimes this is not desirable due to performance considerations or because Client literally does not have all fields available. In order to update only subset of fields you should use HTTP PATCH. PATCH stands for partial resource modification. The standard does not specify exactly how changes must be represented in the request body. If you don’t want to invent a wheel you can use JsonPatchDocument that describes sequences of operations to apply to a JSON document. For instance, if we want to change task summary we need to send the following request.

PATCH http://taskmanager.com/api/tasks/1 HTTP/1.1
Content-Type: application/json-patch+json

[
  {"op":"replace","path":"/summary","value":"New task summary"}
]

In order to add a support of JsonPathDocument in ASP.Net Web API project you can use library Marvin.JsonPatch which is available on NuGet. Server side implementation is below.

[Route("tasks/{taskId}")]
[HttpPatch]
public IHttpActionResult Patch(int taskId, 
[FromBody]JsonPatchDocument<Task> taskPatchDocument)
{
   try
   {
     if (taskPatchDocument == null)
     {
       return BadRequest();
     }

     var task = taskRepository.Find(taskId);
 
     if (task == null)
     {
       return NotFound();
     }

     taskPatchDocument.ApplyTo(task);

     taskRepository.Update(task);

     return Ok(task);
   }
   catch (Exception)
   {
     return InternalServerError();
   }
}

The following line is required in WebApiConfig.Register()

public static void Register(HttpConfiguration config)
{
   config.Formatters.JsonFormatter.SupportedMediaTypes.Add(
     new MediaTypeHeaderValue("application/json-patch+json"));
}

So after we sent PATCH request that changes summary field the response looks like below.

{
 "summary": "New task summary",
 "id": 1,
 "description": "Test task description",
 "lastModified": "2015-11-04T23:42:57.6376424-05:00",
 "created": "2015-11-04T23:42:57.6376424-05:00",
 "userCreated": "UserCreated",
 "assignee": "Assignee",
 "state": 1
}

 

Written by vsukhachev

November 5, 2015 at 5:10 am

Posted in Development

Tagged with ,