Five best practices for ASP.NET Core Web API

Yohan Malshika
5 min readAug 23, 2020

Hi all, Hope you all are doing well. Today I am going to talk about a few best practices for ASP.NET Web API. Some of the best practices I learned from the internship. Because when I joined 99x for my internship, Follow the best practices is one of my goals in the internship targets. So I planned to share some experiences with this blog article.

Five best practices for ASP.NET Core Web API

In this article, I will tell five best practices,

  1. Startup class and the service Configuration
  2. Environment based setting
  3. Clean controllers
  4. Routing
  5. Avoid writing boilerplate code to map objects

1. Startup class and the service Configuration

There are two methods in the startup class that we used for service configuration. They are ConfigureServices and Configure methods. The ConfigureServices method is used for registering the services, and the Configure method is used for adding the middleware components to the application’s pipeline.

The best practice is to keep the ConfigureServices method clean and readable. You know, we have to write code inside the ConfigureServices method. But when we use many services, we have to register services inside the ConfigureServices method, and then it will be not readable or clean. To avoid this situation, we can use extensions methods. The extension method is inherently the static method, and it increases the readability of our code.

Example for the wrong way,

namespace project.test
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
}
}
}

For now, this will be fine. But think when we add more services, this will be not clear.

So the best practice is to use extension methods.

namespace project.test
{
public static class ConfigureExtensions
{
public static void CorsConfiguration(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
}
}
}

after this, we can call this extension method on our startup file like this,

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.CorsConfiguration();
}
}

So now you can see it increase the readability of our code.

2. Environment based setting

When we develop our application, we have to develop our application in the development environment. But when we are going to publish our application, we have to do it in the production environment. Also, after published our application, we have to do update or fix some bugs according to requirements, then we have to do all of these things in the development environment. Therefore having a separate configuration for each environment is always good practice.

This is very easy to do when we use .NET Core. you can see the appsettings.json file in your project folder. If you expand it, then we can see the appsettings.Development.json file. This file is for the development environment.

Also, we should add another file appsettings.Production.json for the production environment.

This is one of the good practices in the .NET Core. when we use a separate appSettings.json file for each environment, we don’t have to change settings in one file for each environment.

3. Clean controllers

Simply the controllers are the brain of the .NET Core application. They process incoming requests, perform operations on Model data, and selects Views to be rendered to the user. So Controllers should always be clean and readable.

It is one of the best practices we should use when creating the .NET Core web API. Basically, we shouldn’t place any business logic inside it. To avoid create business logic inside controllers, we can use a repository pattern.

The repository pattern creates the abstraction between database access and business logic. Instead of writing entire data access logic on the controller, it’s better to write this logic in a different class called a repository. This will make our code more maintainable and understandable.

There is an article that I wrote about Create ASP.NET Core CRUD Web API with the Repository pattern

public class StudentController: Controller
{
private ILoggerManager _logger;
private IRepository _repository;

public StudentController(ILoggerManager logger, IRepository repository)
{
_logger = logger;
_repository = repository;
}

[HttpGet]
public IActionResult GetAllStudent()
{
var students = _repository.GetStudents();
return Ok(students);
}

[HttpGet("{id}")]
public IActionResult GetStudentById(int id)
{
var student = _repository.GetStudentById(id);
return Ok(student);
}

[HttpPost]
public IActionResult CreateStudent([FromBody]StudentDto student)
{
//code
}

[HttpDelete("{id}")]
public IActionResult DeleteStudent(int id)
{
//code
}
}

4. Routing

When we create routing we need to mention the route naming. We can use descriptive names for our actions, but for the routes/endpoints, we should use NOUNS and not VERBS.

Example for wrong-way,

[Route("api/student")]
public class StudentController : Controller
{
[HttpGet("getAllStudents")]
public IActionResult GetAllStudents()
{
}

[HttpGet("getStudentById/{id}"]
public IActionResult GetStudentById(int id)
{
}
}

Example for the right way(best practice)

[Route("api/student")]
public class StudentController : Controller
{
[HttpGet]
public IActionResult GetAllStudents()
{
}

[HttpGet("{id}"]
public IActionResult GetStudentById(int id)
{
}
}

6. Avoid writing boilerplate code to map objects

Basically, we want to keep the separation between our domain models and our view models. For that, we write code that allows us to map our domain model into our view model. But when we have to add more views and domain modelsn we have to write more mappers tmapng our domain models and view models. Then our code will not be clean and readable. To avoid this situation we can use Automapper.

AutoMapper is a convention-based object-to-object mapper that requires little configuration.

For example for the Wrong way, we need to map data transfer objects (DTOs) with the domain objects and vice-versa.

public IActionResult GetStudent(int studentId)
{
var student= studentService.GetStudent(studentId);
var studentDTO = new StudentDTO();
studentDTO.StudentId = student.StudentId;
studentDTO.StudentName = student.StudentName;
studentDTO.StudentAge = student.StudentAge;
//Other code
......
}

Example for best practice (use AutoMapper),

After configuring the AutoMapper, we can map our domain models and view models easily like this.

public class StudentService
{
private StudentRepository studentRepository = new StudentRepository();
public StudentDTO GetStudent (int studentId)
{
var student = studentRepository.GetStudent(studentId);
return Mapper.Map<StudentDTO>(student);
}
//Other methods
}

So now you can see, when we use AutoMapper to map the domain models and view Models, it increases the readability of our code and keeps code cleanly. Also, there are more cool features AutoMapper.

So That’s it for today. I think you learned something new from my article.

See you again soon with another article !!

--

--