Click here to Skip to main content
14,355,397 members

Developing an Extensible Web API

Rate this:
4.80 (4 votes)
Please Sign up or sign in to vote.
4.80 (4 votes)
2 Jul 2019CPOL
Developing a very simple Web API architecture using ASP.NET Web API2 whose operations can be extended by just adding new operation types

  

Introduction

I have recently needed to prepare a Web API which had several small operations, and new ones would be added continuously. Instead of adding many controllers with a few actions in them, I decided to use an extensible architecture. So, new operations could be added only by adding new operation types.

Architecture

The architecture and the project are very simple. The project is created using Web API 2 project template of Visual Studio 2017. There is only one API controller named OperationsController with a single action named Process. There is a simple IOperation interface that an operation must implement and a base class named OperationBase for boilerplate code.

Autofac and Autofac WebAPI2 integration packages are used for automatically discovering operation types and injecting them into the controller.

Code

IOperation interface is as follows:

public interface IOperation
{
    string Name { get; }
    string Description { get; }
    Type ParameterClassType { get; }
    object Execute(object prm);
}

ParameterClassType is the type of the parameter object that Execute method needs. It is used when parsing the JSON data in request body. If Execute method returns a non-null value, it is returned as the result to the caller, otherwise just Ok response is returned.

OperationController and its Process action are as follows:

public class OperationsController : ApiController
{

   //Implemented operations are injected in the constructor
   public OperationsController(IEnumerable<IOperation> supportedOperations)
   {
      _supportedOperations = new List<IOperation>(supportedOperations);
   }

   //Single action that gets the operation name and 
   //reads the operation parameters as JSON from request body

   [HttpPost]
   public async Task<IHttpActionResult> Process(string operationName)
   {
      //Find the operation
      IOperation operation = _supportedOperations.FirstOrDefault(x => x.Name == operationName);
      if (operation == null)
           return BadRequest($"'{operationName}' is not supported.");

      //Get the request body as string
      string jsonBody = await Request.Content.ReadAsStringAsync();
      object operationParams = null;  

      try
      {
          //Parse the JSON data in the request body t construct operation's parameter object
          if (operation.ParameterClassType != null)
                operationParams = Newtonsoft.Json.JsonConvert.DeserializeObject
                                  (jsonBody, operation.ParameterClassType);

          object result = operation.Execute(operationParams);

          //Return the result value as JSON to the caller
          if (result != null)
              return Json(result);
      }
      catch(Exception ex)
      {
           return InternalServerError(ex);
      }

      //Return Ok if the operation has no return value
      return Ok();          
   }    
}

Routing configuration that only needs operationName value is as follows:

config.Routes.MapHttpRoute(
      name: "DefaultApi",
      routeTemplate: "api/{operationName}",
      defaults: new { controller = "operations", action = "process", operationName = "help" }
);

This routing configuration routes all the requests to OperationsControlller's Process action. If there is no operation name provided, help is executed.

Autofac dependency injection is configured and set as the dependency resolver for the Web API pipeline:

ContainerBuilder builder = new ContainerBuilder();

//Register controllers
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

//Register all operations
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
           .Where(t=> t.IsAssignableTo<IOperation>())
           .As<IOperation>();

var container = builder.Build();

//Set Autofac container as the dependency resolver
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

Testing with Postman

Testing parameterless operation "help".

Image 1

Response returned from "help" operation.

Image 2

Testing "sum" operation which expects an object with properties "Number1" and "Number2".

Image 3

Response returned from "sum" operation.

Image 4

Next Steps

To demonstrate only the idea, the code left is very simple. It is good to separate command processing operations from controller class.

Help command only displays the name and description of the operations but it is good to provide whether a particular operation needs parameters and their types with names. Because operations are not controller actions, Swagger will not help for parameter information.

There is also no automatic validation. Validation can be done before the Execute method of the operation, or every operation may perform its own validation logic.

Authentication and authorization operations should also be added for securing the Web API.

History

  • 3rd July, 2019: Initial version

License

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

Share

About the Author

Mustafa Kok
Software Developer (Senior) Freelance
Sweden Sweden
Experienced senior C# developer, sometimes codes in Java and C++ also. He designs and codes desktop applications (WinForms, WPF), windows services and Web APIs (WCF, ASP.Net MVC). He is currently improving his ASP.Net MVC and JavaScript capabilities. He acts as an architect, coder and trainer.
He is mostly experienced in Media Asset Management (MAM) applications, broadcasting sector and medical applications.

LinkedIn: www.linkedin.com/in/mustafa-kok-75352944/
GitHub: github.com/nthdeveloper
StackOverflow: stackoverflow.com/users/1844220/nthdeveloper

Comments and Discussions

 
Praisethanks for the article Pin
FabianSilva2-Jul-19 9:21
memberFabianSilva2-Jul-19 9:21 
PraiseNice one! Pin
Member 145186432-Jul-19 4:46
memberMember 145186432-Jul-19 4:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 2 Jul 2019

Stats

6K views
12 bookmarked