Models in ASP.NET Core Web API

Models in ASP.NET Core Web API

In this article, I will discuss Models in ASP.NET Core Web API Application. Please read our previous article discussing Controllers in ASP.NET Core Web API.

Models in ASP.NET Core Web API

ASP.NET Core Web API is a framework for building web APIs on top of the .NET Core platform. In this context, “Models” are classes that represent the data that the application will handle. These models define the shape of data for requests and responses. Here’s an overview of how models are used in ASP.NET Core Web API:

  • Definition: ASP.NET Core Web API models are typically plain C# classes (POCOs: Plain Old CLR Objects). They represent the data entities and are used to serialize and deserialize data in requests and responses.
  • Creating a Model: A model is usually a simple class with public properties. The properties in the model correspond to the data fields that the API will accept or return. For example, if you have an API for managing books, you might have a Book model with properties like ID, Title, Author, etc.
  • Data Annotations for Validation: Data annotations can be used to define validation rules directly in the model. Common Data Annotation Attributes include [Required], [StringLength], [Range], and more. These Data Annotation Attributes help ensure that the data received by the API meets the specified criteria before the controller actions process it.
  • Usage in Controller Actions: Models are used as parameters in controller actions to receive data from a request (e.g., POST, PUT). They can also be the return type of actions to structure the data sent back to the client. Model binding in ASP.NET Core automatically maps data from the request to the model properties.
  • Model State Validation: ASP.NET Core automatically validates incoming models based on the data annotations. The ModelState.IsValid property can be checked in a controller action to see if the model passed the validation rules. If validation fails, the API can return appropriate error responses.
  • DTOs (Data Transfer Objects): Sometimes, using Data Transfer Objects (DTOs) is beneficial instead of directly exposing the domain models. DTOs can help by including only a subset of the properties or aggregating data from multiple models. This approach enhances security and performance by not exposing more data than necessary and by optimizing the data structure for network transfer.
  • AutoMapper for Model Conversion: AutoMapper or similar libraries can be used to map data between models and DTOs. This simplifies the conversion logic, making the code cleaner and more maintainable.
Creating Model in ASP.NET Core Web API:

In ASP.NET Core Web API, you can create the models anywhere, like within the application root directory, and can be created in a separate class library project, can be created under any folder or subfolder. But as per the recommendation, the models should be created inside the Models folder. So, first, create a folder named Models within the project root directory.

Creating Basic Model:

Now, let us add a basic model. Right-click on the Models folder and then add a class file named Product.cs, and then copy and paste the following code. This is a simple model representing a product. It contains properties like Id, Name, Price, and Category.

namespace MyFirstWebAPIProject.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }
}
Model with Data Annotations

Right-click on the Models folder and then add a class file named User.cs, and then copy and paste the following code. This model represents a user and uses data annotations for validation. The Required and StringLength attributes enforce validation rules.

using System.ComponentModel.DataAnnotations;

namespace MyFirstWebAPIProject.Models
{
    public class User
    {
        public int Id { get; set; }

        [Required]
        [StringLength(100)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(100)]
        public string LastName { get; set; }

        [EmailAddress]
        public string Email { get; set; }
    }
}
Model with Relationships

Right-click on the Models folder and then add a class file named Order.cs, and then copy and paste the following code. The following code shows models with a one-to-many relationship. An Order can have many OrderDetails.

namespace MyFirstWebAPIProject.Models
{
    public class Order
    {
        public int OrderId { get; set; }
        public DateTime OrderDate { get; set; }
        public ICollection<OrderDetail> OrderDetails { get; set; }
    }

    public class OrderDetail
    {
        public int OrderDetailId { get; set; }
        public int OrderId { get; set; }
        public int ProductId { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }

        public Order Order { get; set; }
        public Product Product { get; set; }
    }
}
Model with Enumerations

Right-click on the Models folder and then add a class file named Employee.cs, and then copy and paste the following code.

namespace MyFirstWebAPIProject.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Department Department { get; set; }
    }

    public enum Department
    {
        HR, IT, Finance, Marketing
    }
}
Model with Fluent Validation

Fluent Validation is a popular library for building strongly typed validation rules. It’s separate from data annotations and offers more flexibility. Right-click on the Models folder and then add a class file named Customer.cs, and then copy and paste the following code.

namespace MyFirstWebAPIProject.Models
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
    }
}

Again, right-click on the Models folder, add a class file named CustomerValidator.cs, and copy and paste the following code.

using FluentValidation;
namespace MyFirstWebAPIProject.Models
{
    public class CustomerValidator : AbstractValidator<Customer>
    {
        public CustomerValidator()
        {
            RuleFor(customer => customer.Name).NotEmpty();
            RuleFor(customer => customer.Email).EmailAddress();
            // Other rules
        }
    }
}
Using Models in Controller:

Let us see an example to understand how to use Models in a Controller. So, add a new API Controller – Empty within the Controllers folder named ProductsController and then copy and paste the following code:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MyFirstWebAPIProject.Models;

namespace MyFirstWebAPIProject.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private static List<Product> _products = new List<Product>
        {
            new Product { Id = 1, Name = "Laptop", Price = 1000.00m, Category = "Electronics" },
            new Product { Id = 2, Name = "Desktop", Price = 2000.00m, Category = "Electronics" },
            new Product { Id = 3, Name = "Mobile", Price = 3000.00m, Category = "Electronics" },
            // Add more products
        };

        // GET: api/products
        [HttpGet]
        public ActionResult<IEnumerable<Product>> GetProducts()
        {
            return _products;
        }

        // GET: api/products/2
        [HttpGet("{id}")]
        public ActionResult<Product> GetProduct(int id)
        {
            var product = _products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return product;
        }

        // POST: api/products
        [HttpPost]
        public ActionResult<Product> PostProduct(Product product)
        {
            _products.Add(product);
            return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
        }

        // PUT: api/products/5
        [HttpPut("{id}")]
        public IActionResult PutProduct(int id, Product product)
        {
            if (id != product.Id)
            {
                return BadRequest();
            }

            var existingProduct = _products.FirstOrDefault(p => p.Id == id);
            if (existingProduct == null)
            {
                return NotFound();
            }

            existingProduct.Name = product.Name;
            existingProduct.Price = product.Price;
            existingProduct.Category = product.Category;

            // In a real application, here you would update the product in the database

            return NoContent();
        }

        // DELETE: api/products/5
        [HttpDelete("{id}")]
        public IActionResult DeleteProduct(int id)
        {
            var product = _products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }

            _products.Remove(product);

            // In a real application, here you would delete the product from the database

            return NoContent();
        }
    }
}

Let us understand the above code in detail:

GET Method (Read)

The GET method is used to retrieve data. In this case, you can retrieve either a list of all products or a specific product by its ID. In our example, we have used the following two action methods:

GET Method (Read)

Here,

  • [HttpGet]: Indicates that this action handles HTTP GET requests.
  • ActionResult<IEnumerable<Product>>: The action returns an IEnumerable of Product, wrapped in an ActionResult for HTTP response flexibility.
  • ActionResult<Product>: The action returns a Product, wrapped in an ActionResult for HTTP response flexibility.
  • [HttpGet(“{id}”)]: Specifies that this action responds to a GET request with an id parameter in the route. This method searches for a product with the given id. If not found, it returns a 404 Not Found response.
POST Method (Create)

The POST method is used to create a new product. In our example, we have used the following action method:

POST Method (Create)

Here,

  • [HttpPost]: Indicates handling of POST requests.
  • CreatedAtAction: Returns a 201 Created response, and the Location header includes the URI of the new product.
PUT Method (Update)

The PUT method updates an existing product. In our example, we have used the following action method:

PUT Method (Update)

Here,

  • [HttpPut(“{id}”)]: Specifies that this action responds to a PUT request and expects an id in the route.
  • The method updates the product if it exists or returns a 404 Not Found response if it doesn’t.
DELETE Method (Delete)

The DELETE method removes an existing product. In our example, we have used the following action method:

DELETE Method (Delete)

Here,

  • [HttpDelete(“{id}”)]: Indicates that this action handles DELETE requests with an id parameter.
  • The method deletes the product if it exists or returns a 404 Not Found response if it doesn’t.
Testing the Products Controller APIs:

You can test the APIs in many different ways, like using Postman, Fiddler, and Swagger. But .NET 8 provides the .http file, and using that .http file, we can also test the functionality very easily. This .http file name will be the same as your project name. My project’s name is MyFirstWebAPIProject, so Visual Studio creates the MyFirstWebAPIProject.http file. So, open the .http file and then copy and paste the following code. Please change the port number with the port number on which your application is running.

@MyFirstWebAPIProject_HostAddress = https://localhost:7237

### Get All Products
GET {{MyFirstWebAPIProject_HostAddress}}/api/products
Accept: application/json
###

### Get Product with ID 1
GET {{MyFirstWebAPIProject_HostAddress}}/api/products/1
Accept: application/json
###

### Create a New Product
POST {{MyFirstWebAPIProject_HostAddress}}/api/products
Content-Type: application/json

{
    "Name": "New Product",
    "Price": 39.99,
    "Category": "Books"
}

###

### Update Product with ID 1
PUT {{MyFirstWebAPIProject_HostAddress}}/api/products/1
Content-Type: application/json

{
    "Id": 1,
    "Name": "Updated Product",
    "Price": 49.99,
    "Category": "Electronics"
}

###

### Delete Product with ID 1
DELETE {{MyFirstWebAPIProject_HostAddress}}/api/products/1
###
Testing:

Before testing, ensure your API runs locally and listens on the correct port. Please change the port number to the port number on which your application runs. Open the .http file in your IDE, and you should see “Send Request” links above each HTTP request. Click these links to execute the requests, and you’ll see the responses directly in your IDE, as shown in the below image.

Models in ASP.NET Core Web API Application

Notes:

  • Ensure that your API is running and accessible at the specified URL and port.
  • The Content-Type: application/json header in the requests tells the server that you’re sending JSON data.
  • The format of the JSON payloads in POST and PUT requests should match the expected format of your Product model.

In the next article, I will discuss Routing in ASP.NET Core Web API Application. In this article, I try to explain Models in ASP.NET Core Web API Application. I hope you enjoy this article, “Models in ASP.NET Core Web API.”

Leave a Reply

Your email address will not be published. Required fields are marked *