Developing Api’s using Http Endpoints

When developing web based applications, it will often lead developers to creating web based API’s in order to try and separate back-end and front-end based logic. For most .net based development houses ASP.net MVC is the default choice to develop their API’s. However, over the years i have come to realise that this is not necessarily the right choice.

Often when I open code bases, I see the same mistakes being made time and again along with a number of other common anti-patterns.

MVC Controllers are essentially an anti-pattern. They’re dinosaurs. They are collections of methods that never call one another and rarely operate on the same state. They’re not cohesive. They tend to become bloated and to grow out of control. Their private methods, if any, are usually only called by a single public method. Most developers recognize that controllers should be as small as possible, but they’re the only solution offered out of the box, so that’s the tool 99% of ASP.NET Core developers use.

STEVE SMITH

On a recent project engagement, I think I have discovered the ultimate example of just how poorly developers can implement the Controller approach, along with another 100 or so very poor coding and solution practices. The projects are riddled with issues and suffer with the countless issues that poor code and implementations cause.

Maintenance is difficult, time and resources are stretched. The solution may work, but it is totally unreliable and the software team and the enterprise at large for the most part are going through every scenario that Bob Martin explains in the first chapter of his book Clean Code and eventually opting for The Grand Redesign in the Sky

Eventually the team rebels. They inform management that they cannot continue to develop in this odious code base. They demand a redesign. Management does not want to expend resources on a whole new redesign project, but they cannot deny that productivity is terrible. Eventually they bend to the demands of the developers and authorize the grand redesign in the sky

Bob Martin – Clean Code
Clean Code
A Handbook of Agile Software Craftsmanship

Robert C. Martin

software developers irrespective of programming language or discipline should read this book

Buy Now Read Review

This is often a sign that Technical Debt has become unmanageable and organisations need to take steps to measure levels of technical debt and take steps to reduce technical debt, often this may require re-analysis of the existing solution

ASP.NET Core API Endpoints

Fortunately, on this same project I had the exciting opportunity to start a new series of projects to create new set of APi’s, and the organisation was very keen to on improving and re-evaluating the approach to solution design. I seized the opportunity to illustrate how to implement better alternative approaches to implement API’s making use of Clean Architecture principles and approaches.

Let me first provide an example of what a typical API project and controllers looks like, when developers follow, the out of the box implementation of MVC API controllers, by providing a typical real life example.

This is just one of the supposed Micro Services in the project. Which as you can tell it is far from being Micro, in that the service itself has far too many responsibilities and to be honest, just looks like most peoples lofts or spare rooms – an odd assortment of junk that is collected into one place.

Now we get to the controllers themselves. Lets take a snapshot look of what can typically be found in controller.

C#

The actual file is even longer than than, in fact is over 6 times longer than the above!

Even the most rudimentary glance once over this file, you’ll notice that none of these functions actually belong together in one file and this file resembles the project in that it is also an unsightly collection of junk. Even worse is that this junk is further spread across several other files. Most notably another file named a JobControllerService, which inexplicably seems to do nothing else but shove the controller logic and characteristics to another file for absolutely no benefit.

Warning

As a Contractor/Freelance/Consultant I have seen a number of examples of bad code, in a lot of instances these may not always be put down to bad developers, but often just poor patterns guiding developers down the wrong path. In the case of the above this was certainly a contributory factor, along with some poor architectural choices in solution design in general.

The root cause of a number of these scenarios is organisations and teams not paying enough attention to Technical Debt and not implementing any of the 8 best practices to reduce technical debt

I don’t want to spend too much time analysing and dissecting this code, because it really is a case study for one of the worst code bases I have ever had to work on. Diving deeper into this code continually presents a number of WTF moments.

In my personal opinion the root cause of a number of these problems started right at the beginning when the project started and its initial creation, using the MVC API template!

The MVC Web Api template approach is the worst. It just guides developers down the wrong path, and as a result leads to cluttered and spaghetti code.

Adralis API Endpoints

I have become a big fan of the Adralis ASP.NET Core API Endpoints by Steve Smith as it helps to guide developers down a Clean Architecture Path to developing API Endpoints.

A project for supporting API Endpoints in ASP.NET Core web applications.
https://github.com/ardalis/ApiEndpoints
98 forks.
1,206 stars.
6 open issues.

Recent commits:

The primary reason for this is that it enforces the Single Responsibility Principle

The single responsibility principle (SRP) instructs developers to write code that has one and only one reason to to change. If a class has more than one reason to change . It has more than one respnsibility

Adaptive Code – Agile coding with design principles and SOLID Principles – Gary Maclean
Adaptive Code
Agile coding with design patterns and SOLID principles

Applying principles from this book, will help you create code that accommodates new requirements and unforeseen scenarios without significant rewrites.

Gary Hall Maclean

Buy Now Read Review

Get Started with Ardalis ApiEndpoints

I typically start a new API end point project with a very bare minimum project a very simple project with nothing more than a Startup.cs and Program.cs

New Web Api Project

Then I typically add the following references which are enough to get me started. The point from this Tutorial is the Ardalis.Endpoints

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Ardalis.ApiEndpoints" Version="3.0.0" />
        <PackageReference Include="AutoMapper" Version="10.1.1" />
        <PackageReference Include="Dawn.Guard" Version="1.12.0" />
        <PackageReference Include="MediatR" Version="9.0.0" />
        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.1" />
        <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.1.1" />
        <PackageReference Include="Threenine.Data" Version="2.0.219" />
    </ItemGroup>
</Project>

Due to the fact that we will be developing a REST based API, we probably want to take the time to set up and configure our SwashBuckle Swagger. The key aspect here is that we EnableAnnotations which will help to enrich our documentation for the API.

In Building Microservices: Designing Fine grained systems Sam Newman stresses the importance of when decomposing systems down into finer grained microservices we’ll be exposing seams of functionality in the form of API’s and because of this ease of discovery and good documentation are important. Swagger provides functionality to annotate you API methods and generate the documentation for you.

A Philosophy of Software Design

John Ousterhout

Discusses philosophical issues about how to approach the software design process

Buy Now Read Review
 public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
             services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo {Title = "api", Version = "v1"}); 
                c.EnableAnnotations();
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "api v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
        }
    }
}

With all this grunt work taken care of we can now start coding! lets create our first end point. In this quick example, we will start developing a hypothetical Headless CMS Rest API, we will just simply create an initial endpoint to retrieve a Blog post by passing an Id.

If you want to reduce the amount of grunt work in the future, take a look at How to create project templates in .net core, because I walk you through the process of creating a reusable project template in .net core and the side bonus, I use the APi Endpoints project a template and provide a re-usable template!

This will be just enough the illustrate the basics of Ardalis API Endpoints. So lets get started developing our first Get Endpoint.

The first step is to create a folder to store all your endpoints. It seems the convention for this is call your folder “Endpoints” but I have seem some people/teams call them either one of the following :

  • Enpoints
  • Activities
  • Resources
  • API

for the purpose of this article, and for brevity I am simply going to call them “Endpoints” , then I usually create another folder under neath for my specific routes. In this case, we’ll start with Article

We now create our first Endpoint File, personally I like to name my files after the REST function they are going to fulfill i.e. Get, Post, Put, Patch, Delete. Although this isn’t a hard and fast rule, it just helps me to focus and quickly tell what the goal and purpose of the class at a glance in the project explorer.

The primary purpose my Endpoint is to provide JSON Response Object which will contain a Blog Post and and we’ll Request this by providing some parameters. In this example we will be providing 2 route parameters.

So we need to create Request and Response objects. There appears to be different conventions about where to store these files, but the one that makes sense to me and the one I like to use is actually create them in sub folders under my area of concern endpoint. However, I have also seen and done myself is create folders in my Project root for Requests and Responses. Alternatively I have also extracted these out to their own sub projects. This can be entirely up to you and what makes sense for your project.

Then I create the respective Classes.

Article Request object

The article request object is initially going to contain 2 Parameters. Which will be used to provide the details of the article we would like to get. In our example we will query the Article by Id and Category, which will be provided in the Url Route.

using Microsoft.AspNetCore.Mvc;

namespace api.Endpoints.Article.Request
{
    public class ArticleRequest
    {
        [FromRoute(Name = "id")] public string Id { get; set; }
        [FromRoute(Name = "category")] public string Category { get; set; }
    }
}

We make use of the Model Binding Attributes to indicate where the values will be coming from. Note because I am making use of the Name property and setting the name to lower case. The reason I am doing this is because Model binding is case sensitive, and later on in this article I will illustrate why this is important.

Article Response object

The article response will be a DTO (Data Transmission Object) we will use to provide data back to the calling application. In our case this will contain the basic structure of a blog/article. This may be a simple POCO (Plain Old C# Object) . In our case we’re going to keep this very simple for illustration purposes

using System;

namespace api.Endpoints.Article.Response
{
    public class ArticleResponse
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string Summary { get; set; }
        public string SubHeading { get; set; }
        public string Content { get; set; }
        public DateTime Published { get; set; }
    }
}

Developing the Get Endpoint

We can now get on with developing our end point, and this is where we will now start to make use of the Ardalis Framework. We will create an Asynchronous Endpoint making use of our Request Response objects.

We will make use of the Fluent Generics Pattern to create our Endpoint. fluent interface design is a popular pattern for building flexible and convenient interfaces. Its core idea revolves around using method chaining to express interactions through a continuous flow of human-readable instructions.

Alexey Golub has a great post on Fluent Generics in C#, its well worth a read and in my opinion does a great job in explaining the concept.

We inherit the BaseAsyncEnpoint and provide our Request and Response objects and if we implement the members we will get one method created for us which is the HandleAsync which forms the basic of our Endpoint.

using System.Threading;
using System.Threading.Tasks;
using api.Endpoints.Article.Request;
using api.Endpoints.Article.Response;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Mvc;

namespace api.Endpoints.Article
{
    public class Get  : BaseAsyncEndpoint
        .WithRequest<ArticleRequest>
        .WithResponse<ArticleResponse>
    {
        public override Task<ActionResult<ArticleResponse>> HandleAsync(ArticleRequest request, CancellationToken cancellationToken = new CancellationToken())
        {
            throw new System.NotImplementedException();
        }
    }
}

We’ll add everything to make our API Endpoint fully functional. In this particular case we are simply going to hard code the return object just for illustration purpose.

We will add all the relevant swagger information to help document our endpoint as much as possible.

You may also notice the I have added the async keyword to the HandleAsync , and I also decorated the Request parameter with [FromRoute] attribute. The reason I have done so, is because there seems to be some bug/quirk/irregularity with the ASPNET Core Model binding, which only seems to affect the [FromRoute] parameters, in that it doesn’t always seem to bind them unless Request object itself is marked as [FromRoute] , all other seem to work fine

In further future articles I will provide details on how to mix various Parameters together on a request object. However, for now we will keep it simple.

using System;
using System.Threading;
using System.Threading.Tasks;
using api.Endpoints.Article.Request;
using api.Endpoints.Article.Response;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;

namespace api.Endpoints.Article
{
    [Route("/article")]
    public class Get : BaseAsyncEndpoint
        .WithRequest<ArticleRequest>
        .WithResponse<ArticleResponse>
    {
        public Get()
        {
        }

        [HttpGet("{category}/{id}")]
        [SwaggerOperation(
            Summary = "Retrieve an article by id ",
            Description = "Retrieves a full articles ",
            OperationId = "EF0A3653-153F-4E73-8D20-621C9F9FFDC9",
            Tags = new[] {"Article"})
        ]
        [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ArticleResponse))]
        [Produces("application/json")]
        public override async Task<ActionResult<ArticleResponse>> HandleAsync([FromRoute] ArticleRequest request,
            CancellationToken cancellationToken = new CancellationToken())
        {
           return await Task.Run(() => new OkObjectResult(new ArticleResponse
            {
                Content = "blah blah blah",
                Description = "This is a Fine Description",
                Published = DateTime.Now.AddHours(-10),
                Summary = "this is a fine Summary",
                SubHeading = "This is a sub heading"
            }), cancellationToken);
        }
    }
}

We’re now ready to run our first end point and test it out. If we hit F5 and start he project and open our browser and navigate to http://localhost:5000/swagger. We will see our End Point in all its glory!

If we execute the Endpoint we see our values returned. Our Endpoint behaves just as any other.

Conclusion

Developing API endpoints using the Ardalis API Endpoints framework helps developers to focus on the Clean Code and SOLID principles.

In future articles I will elaborate more on the finer details of using the this framework in helping you to minimise bugs and writing cleaner, maintainable and readable code.

    Like this content ?

    Sign up to my newsletter and I'll send you updates and more!

    Latest posts by Gary Woodfine (see all)