.NET Rest API (Fluent Validation and Code Generator Tool)
Tags: dotnet, api, beginners, programming
Published: 2024-01-02 11:19:01

#### Blog API with EF Core in ASP.NET Core

We will create a blog API with EF Core in ASP.NET Core. We will use the code-first approach to create the database schema. We will use the following tools and technologies:

Visual Studio Code
.NET Core SDK
.NET CLI
In-memory database provider
Entity Framework Core

#### Prerequisites

- Basic knowledge of C#
- Basic knowledge of ASP.NET Core
- Basic knowledge of Entity Framework Core
- Basic knowledge of Visual Studio Code

#### Setup

Create a new folder

```bash
mkdir Blogging
cd Blogging
```

#### Create a new solution

```bash
dotnet new sln
```

The above command will create a new solution file in the Blogging folder.

#### dotnet CLI

Install the dotnet ef tool globally

```bash
dotnet tool install --global dotnet-ef
```

_The above command will install the dotnet ef tool globally. You can check the version of the dotnet ef tool using the following command:_

```bash
dotnet ef --version
```

#### Create a new ASP.NET Core Web API project

```bash
dotnet new webapi --use-controllers -o Blogging
```

_We passed the `--use-controllers` option to the `dotnet new webapi` command to create the project with controllers. The above command will create a new ASP.NET Core Web API project with controllers in the Blogging folder._

#### Add the project to the solution

```bash
dotnet sln add **/*.csproj
```

_The above command will add all the projects in the Blogging folder to the solution file._

#### Build the project

```bash
cd Blogging
dotnet build
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dixksofuchtnptpm4ljo.png)

#### Install the Entity Framework Core tools

cd Blogging and run the following command:

```bash
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.EntityFrameworkCore.SqlServer #this is for SQL Server, it is required for scaffolding the controller
dotnet add package Microsoft.EntityFrameworkCore.Tools
```

<!-- dotnet sln add **/*.csproj    -->

- **Tools**: The tools package contains the EF Core commands for the dotnet CLI. We will use these commands to create the database schema from the model classes.

- **InMemory**: The InMemory package contains the in-memory database provider. We will use this provider to create an in-memory database for testing purposes.
- **Design**: The design package contains the EF Core design-time components. We will use these components to create the database schema from the model classes.

#### Create the model classes

```bash
mkdir Models
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/97gwkk0gyl7bnd6wdzod.png)

Use the `prop` short cut to create properties in the model classes. The prop short cut will create a property with a <mark>getter and a setter</mark> and a private field.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j74fsf6ou6uhy22t1wwt.png)

#### Blog.cs

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blogging.Models
{
    public class Blog
    {
        public int Id { get; set; }
        public string? Url { get; set; } // ? means nullable, i.e. it can be null

        public string? Topic { get; set; }

        public string? Author { get; set; }

        public string? Content { get; set; }

        public DateTime Date { get; set; } = DateTime.UtcNow;
        public DateTime Updated { get; set; } = default!;

    }
}
```

#### DbContext class

We need a connector between the model classes and the database. The DbContext class is a bridge between the model classes and the database. The **DbContext** class is responsible for the following tasks:

- Establishing a connection to the database
- Mapping the model classes to the database tables
- Querying the database
- Saving the data to the database

#### Data folder

Create a new file named BloggingContext.cs in the Data folder and add the following code to it:

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Data
{
    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        {

        }

        public DbSet<Blog> Blogs { get; set; }

    }
}
```

#### Let's understand the above code:

- **DbContext** : The DbContext class is a bridge between the model classes and the database.
- **DbSet** : The DbSet class represents a database table. We will use the DbSet class to query and save data to the database.
- **DbContextOptions** : The DbContextOptions class is used to configure the DbContext class. We will use the DbContextOptions class to configure the database provider, connection string, etc.
- **base(options)** : The base(options) statement is used to pass the options to the base class constructor.

Note: The argument from the base class constructor is passed from the Program.cs file.

#### Register the DbContext class in the Program.cs file

```csharp
using Data;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);


// Registering the DbContext class in the IoC container
builder.Services.AddDbContext<BloggingContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
```

#### Let's understand the above code:

- The **AddDbContext** method is used to register the DbContext class in the IoC container. The AddDbContext method takes a lambda expression as an argument. The lambda expression is used to configure the DbContext class. We have configured the DbContext class to use the in-memory database provider. We have also configured the DbContext class to use the BloggingDatabase as the database name.
- The **AddControllers** method is used to register the controllers in the IoC container.
- The AddEndpointsApiExplorer method is used to register the API explorer in the IoC container.
- The **AddSwaggerGen** method is used to register the Swagger generator in the IoC container.
- The **UseSwagger** method is used to enable the Swagger middleware.
- The **UseSwaggerUI** method is used to enable the Swagger UI middleware.
- The **UseHttpsRedirection** method is used to redirect HTTP requests to HTTPS.

#### Code generation

We will utilize the code generation feature `global dotnet-aspnet-codegenerator` to generate the controller and the views. The code generation feature is available as a global tool. We need to install the global tool to use the code generation feature. We can install the global tool using the following command:

```bash
dotnet tool install --global dotnet-aspnet-codegenerator
```

The above command will install the global tool.

#### Scaffold the controller

Then run:

```bash
dotnet aspnet-codegenerator controller -name BlogsController -async -api -m Blog -dc BloggingContext -outDir Controllers
```

#### Breaking down the above command:

- dotnet aspnet-codegenerator : The dotnet aspnet-codegenerator command is used to generate the controller and the views.
- controller : The controller argument is used to generate the controller.
- -name BlogsController : The -name BlogsController argument is used to specify the name of the controller.
- -async : The -async argument is used to generate the asynchronous action methods.
- -api : The -api argument is used to generate the controller with the API template.
- -m Blog : The -m Blog argument is used to specify the model class.
- -dc BloggingContext : The -dc BloggingContext argument is used to specify the DbContext class.
- -outDir Controllers : The -outDir Controllers argument is used to specify the output directory.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eovys84olef08i9rllhj.png)

If you run the above command, you will get the following error:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4t8k7fwbu0v23441wa17.png)

So we need to install the following packages:

```bash
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
```

Then we need to run the command again:

```bash
dotnet aspnet-codegenerator controller -name BlogsController -async -api -m Blog -dc BloggingContext -outDir Controllers
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdpc2ouogqz7wijr2pnx.png)

The above command will generate the BlogsController.cs file in the Controllers folder. Let's understand the code generated by the code generator.

#### BlogsController.cs

```csharp

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Blogging.Models;
using Data;

namespace Blogging.Controllers
{
    [Route("api/[controller]")]         // This attribute indicates that the controller responds to requests at the api/blogs URL path
    [ApiController]                                      // This attribute indicates that the controller responds to web API requests
    public class BlogsController : ControllerBase
    {
        // The controller's constructor uses dependency injection to inject the database context (BloggingContext) into the controller. The database context is used in each of the CRUD methods in the controller.
        private readonly BloggingContext _context;

        public BlogsController(BloggingContext context)
        {
            _context = context;
        }

        // GET: api/Blogs
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Blog>>> GetBlogs()
        // The return type of the method (Task<ActionResult<IEnumerable<Blog>>>) is an ActionResult type. The ActionResult type is a return type for actions that return an HTTP response with a specific HTTP status code and optional value.
        {
            return await _context.Blogs.ToListAsync(); // The ToListAsync method converts the Blogs DbSet into a List of Blog entities asynchronously.
        }

        // GET: api/Blogs/5
        [HttpGet("{id}")] // The id parameter is passed to the method. The id parameter is defined as a route parameter (in the [HttpGet("{id}")] attribute) and is specified in the route template string for the GetBlog method.
        public async Task<ActionResult<Blog>> GetBlog(int id)
        {
            var blog = await _context.Blogs.FindAsync(id);

            if (blog == null)
            {
                return NotFound();
            }

            return blog;
        }

        // PUT: api/Blogs/5
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPut("{id}")]
        public async Task<IActionResult> PutBlog(int id, Blog blog)
        {
            if (id != blog.Id)
            {
                return BadRequest();
            }

            _context.Entry(blog).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!BlogExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // POST: api/Blogs
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<Blog>> PostBlog(Blog blog)
        {
            _context.Blogs.Add(blog);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetBlog", new { id = blog.Id }, blog);
        }

        // DELETE: api/Blogs/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteBlog(int id)
        {
            var blog = await _context.Blogs.FindAsync(id);
            if (blog == null)
            {
                return NotFound();
            }

            _context.Blogs.Remove(blog);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private bool BlogExists(int id)
        {
            return _context.Blogs.Any(e => e.Id == id);
        }
    }
}
```

Home Controller

#### Run the app

To run with HTTPS support in development, run the following command:

```bash
dotnet dev-certs https --trust

```

If you get an error, run the following command to clean the certificate:

```bash
dotnet dev-certs https --clean
```

Then run the following command:

```bash
dotnet dev-certs https --trust
```

To run the app, run the following command:

```bash
dotnet run --launch-profile https
```

Now Command + Click on the URL to open the app in the browser.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/saalen7dbhg4uqcfkkij.png)

#### Test the API endpoints

Go to https://localhost:<your-port>/swagger/index.html

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vh8j0luqn1b2nd5ed6du.png)

# Create a new blog

Request:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0rv2my81fovnriue87i.png)

Response:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ys71p663q91zbth380pw.png)

#### Add Fluent Validation
Install the Fluent Validation package:
```bash
dotnet add package FluentValidation.AspNetCore
```

Add the following code to the ConfigureServices method in the Program.cs file:

```csharp
builder.Services.AddControllers().AddFluentValidation(s =>
{
    s.RegisterValidatorsFromAssemblyContaining<Program>();
});
```

#### Validate the DTOs using Fluent Validation
It is a good practice to validate the DTOs before passing them to the controller. 
Create a new folder named Dtos.
Create a new file named CreateBlog.cs in the Dtos folder and add the following code to it:

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blogging.Dtos
{
    public class CreateBlog
    {
        public string? Topic { get; set; }

        public string? Author { get; set; }

        public string? Content { get; set; }

    }
}
```

#### Validate the DTOs using Fluent Validation
Create a new file named CreateBlogValidator.cs in the Dtos folder and add the following code to it:

```csharp
using FluentValidation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blogging.Dtos
{
    public class CreateBlogValidator : AbstractValidator<CreateBlog>
    {
        public CreateBlogValidator()
        {
            RuleFor(x => x.Topic).NotEmpty().WithMessage("Topic is required");
            RuleFor(x => x.Author).NotEmpty().WithMessage("Author is required");
            RuleFor(x => x.Content).NotEmpty().WithMessage("Content is required");
        }
    }
}
```


Add the following code to the BlogsController.cs file:

```csharp
using FluentValidation;
using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blogging.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BlogsController : ControllerBase
    {
        private readonly BloggingContext _context;
        private readonly IValidator<CreateBlog> _validator;

        public BlogsController(BloggingContext context, IValidator<CreateBlog> validator)
        {
            _context = context;
            _validator = validator;
        }

        // POST: api/Blogs
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<Blog>> PostBlog(CreateBlog createBlog)
        {
            ValidationResult result = _validator.Validate(createBlog);

            if (!result.IsValid)
            {
                return BadRequest(result.Errors);
            }

            Blog blog = new Blog
            {
                Topic = createBlog.Topic,
                Author = createBlog.Author,
                Content = createBlog.Content
            };

            _context.Blogs.Add(blog);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetBlog", new { id = blog.Id }, blog);
        }
    }
}
```


#### Create a Custom Api Response to return the validation errors
Create a new file in the models folder named ApiResponse.cs and add the following code to it:

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using System.Text.Json.Serialization;

namespace Blogging.Models
{
    public class ApiResponse
    {
        public bool Status { get; set; }
        public string Message { get; set; }
        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
        public object? Data { get; set; }
        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
        public List<string>? Errors { get; set; }
    }
}
```

#### Update the BlogsController.cs file
Update the BlogsController.cs file as shown below:

```csharp
using FluentValidation;
using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blogging.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BlogsController : ControllerBase
    {
        private readonly BloggingContext _context;
        private readonly IValidator<CreateBlog> _validator;

        public BlogsController(BloggingContext context, IValidator<CreateBlog> validator)
        {
            _context = context;
            _validator = validator;
        }

        // POST: api/Blogs
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<Blog>> PostBlog(CreateBlog createBlog)
        {
            ValidationResult result = _validator.Validate(createBlog);

            if (!result.IsValid)
            {
                return BadRequest(new ApiResponse { Status = false, Errors = result.Errors.Select(e => e.ErrorMessage).ToList() });
            }

            Blog blog = new Blog
            {
                Topic = createBlog.Topic,
                Author = createBlog.Author,
                Content = createBlog.Content
            };

            _context.Blogs.Add(blog);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetBlog", new { id = blog.Id }, blog);
        }
    }
}
```

#### Conclusion
In this article, we learned how to create a blog API with EF Core in ASP.NET Core. We learned how to use the code-first approach to create the database schema. We learned how to use the following tools and technologies:

- Visual Studio Code
- .NET Core SDK
- .NET CLI
- In-memory database provider
- Entity Framework Core
- Fluent Validation

#### Watch the video version here:
[![Fluent Validation](https://img.youtube.com/vi/dRlwHkXS8-g/0.jpg)](https://youtu.be/dRlwHkXS8-g)


If you have any questions or feedback, then please drop a comment below. Thank you for reading. Cheers!