Introduction
Microservices architecture got its reputation in software development since it was introduced and today, it is a very popular architectural style approach to build apps and is used by many organizations. When taking a microservices approach, you would normally break product features into services or APIs to perform specific tasks, and .NET Core is a perfect candidate to build these kinds of services.
Building APIs that can be deployed and hosted anywhere is pretty much easy nowadays when .NET Core came into life. However, if you will be building a lot of APIs to support various product features within a short timeframe, then that’s how things can get troublesome. This is because each developer/team will have their own way of building APIs and has their own standard base structure of the project. You may probably have to do a lot of copy and paste to have a consistent base structure of your API
project and configure its core dependencies all over again which can eat a lot of development time. With these reasons, ApiBoilerPlate was created.
The Project Template
Today, I'm happy to announce that the ApiBoilerPlate
is now officially released and can now be downloaded and installed in Visual Studio 2019 from Visual Studio Market place or via .NET CLI. I made this project open-source to allow fellow developers to contribute on it for improvements.
ApiBoilerPlate
is a simple yet organized project template for building ASP.NET Core APIs using .NET Core 3.x (the latest/fastest version of .NET Core to date) with preconfigured tools and frameworks. The goal is to help you get up to speed when setting up the core structure of your app and its dependencies. This enables you to focus on implementing business specific requirements without you having to copy and paste the core structure of your project, and installing its dependencies all over again. This will speed up your development time when building new API project while enforcing standard project structure with its dependencies and configurations for all your apps.
If you are looking for a project template for ASP.NET Core API that you can reuse across your team, or if you are new to ASP.NET Core and would like to get up to speed on how it works without having you to configure most of the basic features that an API will have, then this is for you.
How to get it?
There are two ways to install the template:
Keep in mind that you can always replace and choose whatever framework you want to use for your API
. After all, the template is just a skeleton for your project structure with default preconfigured middlewares. For example, you can always replace Dapper
with EntityFramework Core
, PetaPoco
, etc. and configure them yourself. You can also replace Serilog
with whatever logging frameworks and providers you want that works with ASP.NET Core
- the choice is yours.
Install the Template from .NET CLI
- Install the latest .NET Core SDK.
- Run
dotnet new -i apiboilerplate.aspnetcore
. This will install the template in your machine. - Run
dotnet new apiboilerplate --name "MyAPI"; -o samples
. This will generate the project template named MyAPI
within the samples
directory.
Once installed, you should see this output below:
Quote:
The template "ASP.NET Core API Template for .NET Core 3.x" was created successfully.
Install the Template from the Visual Studio Marketplace
- Fire up Visual Studio 2019, click "Continue without code" link
- On the "Extensions" menu, click "Manage Extensions".
- Click "Online" and then search for "ApiBoilerPlate".
- Click "Download". The extension is then scheduled for install. Close all instances of Visual Studio.
- The VISX (Extension) installer should popup. Click "Modify" to begin installing the template as shown in the figure below:
-
Click "Close" after modification is complete.
Alternatively, you can download and install the VSIX Extension directly at the following link: https://marketplace.visualstudio.com/items?itemName=vmsdurano.ApiProjVSExt
Create a new Project from ApiBoilerPlate Template
- Open Visual Studio 2019 and then select "Create a New Project" box
- The newly installed template should appear at the top. You can also type "ApiBoilerPlate" in the search bar:
- Click the "ApiBoilerPlate" item and then click "Next".
- Name your project to whatever you like and then click "Create".
- Visual Studio should generate the files for you as shown in the figure below.
The generated files contain the skeleton structure of the project with a very basic sample implementation for you get started working on your APIs.
Steps to run the Template
STEP 1: Create a Test local Database:
- Open Visual Studio 2019
- Go to View > SQL Server Object Explorer
- Drilldown to SQL Server > (localdb)\MSSQLLocalDB
- Right-click on the "Database" Folder
- Click "Add New Database"
- Name it as "
TestDB
" and click OK - Under "TestDB", Right-click on the "Tables" folder and select "Add New Table". Or you could simply right-click on the "TestDB" database, select "New Query" and run the script below to generate the "
Person
" table. - Name the table as "Person" with the following fields:
CREATE TABLE [dbo].[Person]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(1,1),
[FirstName] NVARCHAR(20) NOT NULL,
[LastName] NVARCHAR(20) NOT NULL,
[DateOfBirth] DATETIME NOT NULL
)
STEP 2: Update Database ConnectionString (optional)
If you follow step 1, then you can skip this step and run the application right away.
If you have a different database
and table
name then you need to change the connectionString in appsettings.json that is pointing to the newly created database. You can get the connectionString
values in the properties window of the "TestDB" database in Visual Studio.
Testing the Default API Controller
After setting up the database, it’s time to test the API
that was preconfigured for you. Run the application and navigate to: https://localhost:44321/swagger
(See Properties > launchSettings.json to know how the application launching was configured)
Now, you should be presented with the Swagger UI documentation page as shown below:
Swagger provides an advance documentation for your APIs where it allows you to look the details of your API endpoints and test them when necessary. The template is preconfigured to enable this feature by default using Swashbuckle.AspNetCore tool. This means that everytime you add API
endpoints to your project, swagger will automatically generate this document without doing anything in your part. This document is very helpful as a reference especially when your APIs are publicly available and you expect many developers using it.
Another good thing about the Swagger UI, is it allows you to test your APIs. For example, you can POST
a request to your API
and get the response back. To see that in action let’s make a POST
request with the following body parameters:
{
"firstName": "Vianne Maverich",
"lastName": "Durano",
"dateOfBirth": "2019-09-27T23:27:50.076Z"
}
Clicking the Execute
button gives you the following response:
Response Body:
{
"message": "Created Successfully",
"isError": false,
"result": 1
}
Response Header:
content-type: application/json
date: Fri, 27 Sep 2019 23:31:35 GMT
server: Microsoft-IIS/10.0
status: 201
x-powered-by: ASP.NET
To verify if the data was inserted to the database, you can invoke the GET
method to see the results. For this example, it should return you the following JSON payload:
{
"message": "Request successful.",
"isError": false,
"result": [
{
"id": 1,
"firstName": "Vianne Maverich",
"lastName": "Durano",
"dateOfBirth": "2019-09-27T23:27:50.077"
}
]
}
If you notice, the HTTP
response was automatically formatted for you. The template uses AutoWrapper middleware to wrap each HTTP
responses for both success and failure without doing anything on your side. For more information, see: AutoWrapper: Prettify Your ASP.NET Core APIs with Meaningful Responses
Tip: Postman is also a good alternative to test APIs
and I still use that.
Logging
Logging is also preconfigured for you. It uses Serilog.AspNetCore
to log data. By default, the template is preconfigured to log data in Console
and File
. You can find the logs at the Logs
folder. Here’s a sample of logs that was captured based on this example:
2019-09-27T18:08:48.0418932-05:00 [INF] () Starting web host
2019-09-27T18:31:35.6179254-05:00 [INF] (AutoWrapper.AutoWrapperMiddleware) Request: POST https localhost:44321/api/v1/Persons {"firstName":"Vianne Maverich","lastName":"Durano","dateOfBirth":"2019-09-27T23:27:50.076Z"} Responded with [201] in 968ms
2019-09-27T18:34:38.6852072-05:00 [INF] (AutoWrapper.AutoWrapperMiddleware) Request: GET https localhost:44321/api/v1/Persons Responded with [200] in 87ms
To see how the log is configured, see the appsettings.logs.json
file from the project template.
Walkthrough
If you got this far, then I’m assuming you got the application up and running. At this point, you might be wondering where to start modifying the template to add your code and feel a bit confused. In this section, I’ll try my best to walk you through.
Data Folder
The first thing that you may want to look at in the template is the Data
folder, which contains a class called "DBFactoryBase
". This class is an abstract class that implements a few methods to perform basic database operations:
namespace ApiBoilerPlate1.Data
{
public abstract class DBFactoryBase
{
private readonly IConfiguration _config;
public DBFactoryBase(IConfiguration config)
{
_config = config;
}
internal IDbConnection DbConnection
{
get {
return new SqlConnection(_config.GetConnectionString("SQLDBConnectionString"));
}
}
public virtual async Task<IEnumerable<T>> DbQueryAsync<T>(string sql, object parameters = null)
{
using (IDbConnection dbCon = DbConnection)
{
dbCon.Open();
if (parameters == null)
return await dbCon.QueryAsync<T>(sql);
return await dbCon.QueryAsync<T>(sql, parameters);
}
}
public virtual async Task<T> DbQuerySingleAsync<T>(string sql, object parameters)
{
using (IDbConnection dbCon = DbConnection)
{
dbCon.Open();
return await dbCon.QueryFirstOrDefaultAsync<T>(sql, parameters);
}
}
public virtual async Task<bool> DbExecuteAsync<T>(string sql, object parameters)
{
using (IDbConnection dbCon = DbConnection)
{
dbCon.Open();
return await dbCon.ExecuteAsync(sql, parameters) > 0;
}
}
public virtual async Task<bool> DbExecuteScalarAsync(string sql, object parameters)
{
using (IDbConnection dbCon = DbConnection)
{
dbCon.Open();
return await dbCon.ExecuteScalarAsync<bool>(sql, parameters);
}
}
}
}
It uses Dapper
as a data access mechanism to communicate with the database. Again, you are free to modify it as you please. The main reason why I opt for Dapper
is because of its speed and simplicity. If you value convenience over performance, then you can always replace your data access with other full-blown ORM such as Entity Framework Core
.
Contracts Folder
The next place to look at is the Contracts
folder. This is where you add all your interfaces
that are needed for your application. By default, it contains three (3) Interfaces:
The IRepository Interface
namespace ApiBoilerPlate1.Contracts
{
public interface IRepository<T>
{
Task<IEnumerable<T>> GetAllAsync();
Task<T> GetByIdAsync(object id);
Task<long> CreateAsync(T entity);
Task<bool> UpdateAsync(T entity);
Task<bool> DeleteAsync(object id);
Task<bool> ExistAsync(object id);
}
}
We don't want our API Controller
to access directly the DBFactoryBase
class, instead we will let other classes handle the communication between our DBFactoryBase
and API Controllers
The code above defines a simple generic repository interface. An interface
is just a skeleton of a method without the actual implementation.
The IPersonManager Interface
using ApiBoilerPlate1.Domain.Entity;
namespace ApiBoilerPlate1.Contracts
{
public interface IPersonManager : IRepository<Person>
{
}
}
The code above is an interface for managing the Person
entity as an example. Notice that it implements the IRepository
interface so that this interface can inherit all the available methods from the generic repository. This interface will be injected into the API Controller
so we will only need to talk to the interface rather than the actual implementation of the repository. One of the main benefits of using interface is to make our code reusable, testable, maintainable and taking advantage of Dependency Injection.
The IServiceRegistration Interface
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ApiBoilerPlate1.Contracts
{
public interface IServiceRegistration
{
void RegisterAppServices(IServiceCollection services, IConfiguration configuration);
}
}
The code above is an interface that will be used for services that needs to be added in IServiceCollection
.
Domain Folder
The next place to look at is the Domain
folder. This is where we implement the actual database operation and its associated logic. This folder has a sub-folder called "Entity
" wherein you define all your models for your database. Here’s the Person
entity model example:
namespace ApiBoilerPlate1.Domain.Entity
{
public class Person
{
public long ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
}
The code above is nothing but a plain class that houses a few properties. The class name represents the database table name, and the properties represents the table columns.
The PersonManager
namespace ApiBoilerPlate1.Domain
{
public class PersonManager : DBFactoryBase, IPersonManager
{
public PersonManager(IConfiguration config) : base(config)
{
}
public async Task<IEnumerable<Person>> GetAllAsync()
{
return await DbQueryAsync<Person>("SELECT * FROM Person");
}
public async Task<Person> GetByIdAsync(object id)
{
return await DbQuerySingleAsync<Person>("SELECT * FROM Person WHERE ID = @ID", new { id });
}
public async Task<long> CreateAsync(Person person)
{
string sqlQuery = $@"INSERT INTO Person (FirstName, LastName, DateOfBirth)
VALUES (@FirstName, @LastName, @DateOfBirth)
SELECT CAST(SCOPE_IDENTITY() as bigint)";
return await DbQuerySingleAsync<long>(sqlQuery, person);
}
public async Task<bool> UpdateAsync(Person person)
{
string sqlQuery = $@"IF EXISTS (SELECT 1 FROM Person WHERE ID = @ID)
UPDATE Person SET FirstName = @FirstName, LastName = @LastName, DateOfBirth = @DateOfBirth
WHERE ID = @ID";
return await DbExecuteAsync<bool>(sqlQuery, person);
}
public async Task<bool> DeleteAsync(object id)
{
string sqlQuery = $@"IF EXISTS (SELECT 1 FROM Person WHERE ID = @ID)
DELETE Person WHERE ID = @ID";
return await DbExecuteAsync<bool>(sqlQuery, new { id });
}
public async Task<bool> ExistAsync(object id)
{
return await DbExecuteScalarAsync("SELECT COUNT(1) FROM Person WHERE ID = @ID", new { id });
}
}
}
The code above is a concrete class that implements the DBFactorBase
base class and IPersonManager
interface that we have just created earlier. This is where you implement domain specific log and database operations.
DTO Folder
Data Transfer Object (a.k.a DTO
) are classes that defines the model with predefined validation in place. DTOs
will be passed as the parameters to your API
endpoints or use it as a return object for results. The basic idea about having a DTO
is to decouple the validation from the Entity
models and control the data that you want to allow the client to see.
Here’s an example of the PersonDTO
:
using FluentValidation;
using System;
namespace ApiBoilerPlate1.DTO
{
public class PersonDTO
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class PersonDTOValidator : AbstractValidator<PersonDTO>
{
public PersonDTOValidator()
{
RuleFor(o => o.FirstName).NotEmpty();
RuleFor(o => o.LastName).NotEmpty();
RuleFor(o => o.DateOfBirth).NotEmpty();
}
}
}
The class above defines validations for the model. It uses FluentValidation.AspNetCore
to provide a clean, flexible and strongly-typed validation rules.
Helpers Folder
The next place to look at is the Helpers
folder. This is where you add any class that performs common tasks. For example, you can add Utility or Extension classes in this folder. By default it contains an Extensions
folder and a MapperProfile
class.
The MappingProfile Class
using AutoMapper;
using ApiBoilerPlate1.Domain.Entity;
using ApiBoilerPlate1.DTO;
namespace ApiBoilerPlate1.Helpers
{
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Person, PersonDTO>().ReverseMap();
}
}
}
The class above is where you define the mappings between your DTOs
and Entity
models. It uses AutoMapper
to map between objects. In this example, we created a mapping between a Person
entity and a PersonDTO
model.
Extensions Folder
This folder is where you add classes for your extension methods in your project. By default, it contains a ServiceRegistrationExtension
class with an extension method AddServicesInAssembly()
which is responsible for adding all available services within the assembly. This class will be called in the ConfigureServices()
method of Startup.cs file.
Here’s the ServiceRegistrationExtension
implementation:
namespace ApiBoilerPlate1.Helpers.Extensions
{
public static class ServiceRegistrationExtension
{
public static void AddServicesInAssembly(this IServiceCollection services, IConfiguration configuration)
{
var appServices = typeof(Startup).Assembly.ExportedTypes
.Where(x => typeof(IServiceRegistration)
.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.Select(Activator.CreateInstance)
.Cast<IServiceRegistration>().ToList();
appServices.ForEach(svc => svc.RegisterAppServices(services, configuration));
}
}
}
Installers Folder
This is where you add services that you would want to be configured on application start up. By default, it contains two classes:
The RegisterContractMappings Class
namespace ApiBoilerPlate1.Installers
{
public class RegisterContractMappings : IServiceRegistration
{
public void RegisterAppServices(IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<IPersonManager, PersonManager>();
}
}
}
The class above is responsible for registering all interface mappings between your contract repositories and concrete classes that implement the contracts.
The RegisterModelValidators Class
namespace ApiBoilerPlate1.Installers
{
public class RegisterModelValidators : IServiceRegistration
{
public void RegisterAppServices(IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<IValidator<PersonDTO>, PersonDTOValidator>();
}
}
}
The class above is responsible for registering all validators for your DTO
models.
Notice that both classes above implement the IServiceRegistration
interface so that they will be configured automatically upon calling the AddServicesInAssembly()
extension method defined in ServiceRegistrationExtension
class.
API Folder
Now that we have most of the pieces in place, the next place to look at is the API
folder. This folder contains a sub-folder called "v1
" to organize Controllers
into versions. By default, the v1
folder contains the PersonsController
with the following implementation:
namespace ApiBoilerPlate1.API.v1
{
[Route("api/v1/[controller]")]
[ApiController]
public class PersonsController : ControllerBase
{
private readonly ILogger<PersonsController> _logger;
private readonly IPersonManager _personManager;
private readonly IMapper _mapper;
public PersonsController(IPersonManager personManager, IMapper mapper, ILogger<PersonsController> logger)
{
_personManager = personManager;
_mapper = mapper;
_logger = logger;
}
[HttpGet]
public async Task<IEnumerable<Person>> Get()
{
return await _personManager.GetAllAsync();
}
[Route("{id:long}")]
[HttpGet]
public async Task<Person> Get(long id)
{
var person = await _personManager.GetByIdAsync(id);
if (person != null)
{
return person;
}
else
throw new ApiException($"Record with id: {id} does not exist.", 204);
}
[HttpPost]
public async Task<ApiResponse> Post([FromBody] PersonDTO dto)
{
if (ModelState.IsValid)
{
try
{
var person = _mapper.Map<Person>(dto);
return new ApiResponse("Created Successfully", await _personManager.CreateAsync(person), 201);
}
catch (Exception ex)
{
_logger.Log(LogLevel.Error, ex, "Error when trying to insert.");
throw;
}
}
else
throw new ApiException(ModelState.AllErrors());
}
[Route("{id:long}")]
[HttpPut]
public async Task<ApiResponse> Put(long id, [FromBody] PersonDTO dto)
{
if (ModelState.IsValid)
{
try
{
var person = _mapper.Map<Person>(dto);
person.ID = id;
if (await _personManager.UpdateAsync(person))
return new ApiResponse("Update successful.", true);
else
throw new ApiException($"Record with id: {id} does not exist.", 400);
}
catch (Exception ex)
{
_logger.Log(LogLevel.Error, ex, "Error when trying to update with ID:{@ID}", id);
throw;
}
}
else
throw new ApiException(ModelState.AllErrors());
}
[Route("{id:long}")]
[HttpDelete]
public async Task<bool> Delete(long id)
{
try
{
var isDeleted = await _personManager.DeleteAsync(id);
if (isDeleted)
{
return isDeleted;
}
else
throw new ApiException($"Record with id: {id} does not exist.", 400);
}
catch (Exception ex)
{
_logger.Log(LogLevel.Error, ex, "Error when trying to delete with ID:{@ID}", id);
throw;
}
}
}
}
The class above defines the API
endpoints and its routes with preconfigured implementation as an example. For more information about creating APIs ASP.NET Core, see: Create web APIs with ASP.NET Core
Workers Folder
As an additional feature, the template includes a default example on how to run a background tasks within ASP.NET Core. Here’s the simple implementation of a worker service:
namespace ApiBoilerPlate1.Workers
{
public class MessageQueueProcessorService : BackgroundService
{
private readonly ILogger<MessageQueueProcessorService> _logger;
public MessageQueueProcessorService(ILogger<MessageQueueProcessorService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"MessageQueueProcessorService is starting.");
stoppingToken.Register(() => _logger.LogDebug($" MessageQueueProcessorService background task is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogDebug($"MessageQueueProcessorService task doing background work.");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
_logger.LogDebug($"MessageQueueProcessorService background task is stopping.");
}
}
}
The code above simulates a simple background task that keeps on running every 5 seconds. So, if there’s a need for you to implement a worker service, then this should provides you an idea on how to get started.
For more information about running a background tasks with hosted services in ASP.NET Core, See: Hosted Services in ASP.NET Core
Wrap Up
To see how each of the services in the template are configured, then take a look at the Startup.cs and Program.cs classes. To give you a quick overview, here’s how the ConfigureServices()
method of Startup.cs file looks like :
public void ConfigureServices(IServiceCollection services)
{
services.AddServicesInAssembly(Configuration);
services.Configure<ApiBehaviorOptions>(opt => { opt.SuppressModelStateInvalidFilter = true; });
services.AddControllers()
.AddFluentValidation(fv => { fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false; });
services.AddAutoMapper(typeof(Helpers.MappingProfile));
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ASP.NET Core Template API", Version = "v1" });
});
}
For more information on how the Startup
and Program
class works, read: ASP.NET Core Fundamentals
Summary
In this post, we’ve learned how to use the ApiBoilerPlate
project template for building APIs in .NET Core 3.0. We've also learned about its structure and how it was implemented.
I’m pretty sure there are still lots of things to improve in this project, so feel free to try it out and let me know your thoughts. Comments and suggestions are welcome, please drop a message and I’d be happy to answer any queries as I can.
Source code can be found here: https://github.com/proudmonkey/ApiBoilerPlate