Best Practice in Creating Web API Services

25. April 2017 Web API 0

This is the second part of Web API Series. In the first part, we’ve learnt how to create a RESTful API with Web API. In this tutorial, we gonna learn how we can enhance the code and provides more flexibility and simplicity to the code.

Overview

The following code snippet is the implementation of our Web API service:

[RoutePrefix("api/employee")]
public class EmployeeController : ApiController
{
    [Route("")]
    [HttpPost]
    public HttpResponseMessage Create(Employee employee)
    {
        try
        {
            var bc = new EmployeeBusiness();
            var result = bc.Create(employee);

            return Request.CreateResponse(HttpStatusCode.Created, result);
        }
        catch (Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
        }
    }

    [Route("{id}")]
    [HttpGet]
    public HttpResponseMessage Get(int id)
    {
        try
        {
            var bc = new EmployeeBusiness();
            var result = bc.Get(id);

            return Request.CreateResponse(HttpStatusCode.OK, result);
        }
        catch (Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
        }
    }

    [Route("")]
    [HttpGet]
    public HttpResponseMessage List()
    {
        try
        {
            var bc = new EmployeeBusiness();
            var result = bc.List();

            return Request.CreateResponse(HttpStatusCode.OK, result);
        }
        catch (Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
        }
    }

    [Route("")]
    [HttpPut]
    public HttpResponseMessage Update(Employee employee)
    {
        try
        {
            var bc = new EmployeeBusiness();
            bc.Update(employee);

            return Request.CreateResponse(HttpStatusCode.Accepted);
        }
        catch (Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
        }
    }

    [Route("{id}")]
    [HttpDelete]
    public HttpResponseMessage Remove(int id)
    {
        try
        {
            var bc = new EmployeeBusiness();
            bc.Remove(id);

            return Request.CreateResponse(HttpStatusCode.Accepted);
        }
        catch (Exception ex)
        {
            return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
        }
    }
}

As you can see there all the methods are repeating the same thing. They all call a business method and return the result along with the status code. They also check for any exception and wrap the exception in the response object. The repeated task can be delegated to a base class which accepts an action, performs it and returns the result.

Download the EmployeeManagement project if you don’t have it already.

Create a Service Base Class

Let’s create the base class. In the EmployeeManagement.Service, right click on the Controller folder, Add, and then select Class. Name it ServiceBase.cs.

This class will be all services’ base class so it needs to inherit from ApiContoller.

public class ServiceBase : ApiController

We have two types of actions: those which return a result (like Get(), List() or etc. methods), and those which just perform an action (like Update() or Remove() methods). For this purpose, we need to create two methods in our base class. One to get a Func<> as parameter to service those methods returning a result and another one to get an Action as parameter which calls void methods.

Below is the implementation of these two methods:

protected internal HttpResponseMessage ServiceAction<TResult>(Func<TResult> action, HttpStatusCode successCode = HttpStatusCode.OK)
{
    try
    {
        var result = action();

        return Request.CreateResponse(successCode, result);

    }
    catch (Exception ex)
    {
        return Request.CreateResponse(HttpStatusCode.InternalServerError, ex);
    }
}

protected internal HttpResponseMessage ServiceAction(Action action, HttpStatusCode successCode = HttpStatusCode.OK)
{
    try
    {
        action();

        return Request.CreateResponse(successCode);
    }
    catch (Exception ex)
    {
        return Request.CreateResponse(HttpStatusCode.InternalServerError, ex);
    }
}

The first method take Func<TResult> as parameter. In the method’s body, we call the action and hold the result in a variable. We then wrap the result in response object with the optional statusCode value (set to OK by default). The second method gets an Action rather than the Func<>. It does the same thing, except, the action yields no results. So we are just calling the action and return the status code as a response. Both methods wrap the execution in try-catch block and returns InternalServerError along with the exception.

Modify the Web API Methods

Next, we need to modify our Web API methods to use the base class. Remove the inheritance from ApiController and instead inherit from ServiceBase class.

[RoutePrefix("api/employee")]
public class EmployeeController : ServiceBase

Modify the service operations as below:

[RoutePrefix("api/employee")]
public class EmployeeController : ServiceBase
{
    EmployeeBusiness _bc;

    public EmployeeController()
    {
        _bc = new EmployeeBusiness();
    }

    [Route("")]
    [HttpPost]
    public HttpResponseMessage Create(Employee employee) => ServiceAction(() => { return _bc.Create(employee); });

    [Route("{id}")]
    [HttpGet]
    public HttpResponseMessage Get(int id) => ServiceAction(() => { return _bc.Get(id); });

    [Route("")]
    [HttpGet]
    public HttpResponseMessage List() => ServiceAction(() => { return _bc.List(); });

    [Route("")]
    [HttpPut]
    public HttpResponseMessage Update(Employee employee) => ServiceAction(() => { _bc.Update(employee); }, HttpStatusCode.Accepted);

    [Route("{id}")]
    [HttpDelete]
    public HttpResponseMessage Remove(int id) => ServiceAction(() => { _bc.Remove(id); }, HttpStatusCode.Accepted);
}

You can see how our service implementation became much simpler, in fact, just one line.

We only pass the second parameter, i.e. status code, when the status code we want to return is something else than OK (200). For example, HttpStatusCode.Accepted (202) for Update and Delete operations, and HttpStatusCode.Created (201) for Create.

Download

Download the sample code


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.