How To Build RESTful APIs With ASP.NET Core 8
Tags: webdev, aspnet, csharp, api
Published: 2024-06-28 14:57:10

## Introduction

Modern software development often requires building RESTful APIs. With the release of ASP.NET Core 8, building robust scalable APIs has never been easier. In this blog post, we'll explore how to build RESTful APIs using ASP.NET Core 8, including in-depth points, code samples, and personal insights. Whether you are a veteran developer or just starting out, you will find valuable information here to enhance your development experience.

RESTful APIs (Representational State Transfer) have become the standard for web services. They provide a simple, scalable way to expose data and functionality over HTTP. ASP.NET Core, a cross-platform, high-performance framework, is an excellent choice for building these APIs. With the release of ASP.NET Core 8, we get even more features and improvements to enhance our development experience.

> For those interested in learning more about .NET development, check out our [.NET Development](https://wirefuture.com/blog/dot-net-development) blogs. Stay updated with the latest insights and best practices!

## Why RESTful APIs?

Before diving into the technical details, let's take a moment to understand why RESTful APIs are so popular. RESTful APIs:

1. **Are easy to understand and use:** They use standard HTTP methods and status codes, making them accessible to developers of all levels.

2. **Support a wide range of clients:** From web applications to mobile apps and IoT devices, RESTful APIs can be consumed by virtually any client that can make HTTP requests.

3. **Promote stateless communication:** Each request from a client contains all the information needed to process it, allowing for scalable and resilient systems.

4. **Encourage good design practices:** Using REST principles, you can design clean, intuitive APIs that are easy to maintain and evolve.

## Getting Started with ASP.NET Core 8

To start building a RESTful API with ASP.NET Core 8, you'll need to set up your development environment. Make sure you have the following installed:

- .NET 8 SDK
- Visual Studio or Visual Studio Code
- A modern web browser for testing

## Creating a New ASP.NET Core Project

Open your terminal or command prompt and run the following command to create a new ASP.NET Core project:

```
dotnet new webapi -n MyApi
cd MyApi
```

This command creates a new ASP.NET Core Web API project named MyApi and navigates into the project directory. Open the project in your preferred IDE.

## Project Structure

Your project will have the following structure:

```
MyApi
├── Controllers
│   └── WeatherForecastController.cs
├── Program.cs
├── Startup.cs
└── MyApi.csproj
```
The Controllers folder contains the default WeatherForecastController. We'll create our own controllers shortly.

## Building Your First API Endpoint

Let's create a simple API endpoint that returns a list of products. Start by defining a Product model.

**Defining the Product Model**

Create a new folder named Models and add a Product.cs file with the following content:

```
namespace MyApi.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}
```

**Creating the Products Controller**

Next, create a Controllers folder if it doesn't exist and add a ProductsController.cs file:

```
using Microsoft.AspNetCore.Mvc;
using MyApi.Models;
using System.Collections.Generic;
using System.Linq;

namespace MyApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        private static readonly List<Product> Products = new List<Product>
        {
            new Product { Id = 1, Name = "Laptop", Price = 999.99M },
            new Product { Id = 2, Name = "Smartphone", Price = 499.99M }
        };

        [HttpGet]
        public ActionResult<IEnumerable<Product>> GetProducts()
        {
            return Ok(Products);
        }

        [HttpGet("{id}")]
        public ActionResult<Product> GetProduct(int id)
        {
            var product = Products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }

        [HttpPost]
        public ActionResult<Product> CreateProduct(Product product)
        {
            product.Id = Products.Count + 1;
            Products.Add(product);
            return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
        }

        [HttpPut("{id}")]
        public IActionResult UpdateProduct(int id, Product updatedProduct)
        {
            var product = Products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }

            product.Name = updatedProduct.Name;
            product.Price = updatedProduct.Price;
            return NoContent();
        }

        [HttpDelete("{id}")]
        public IActionResult DeleteProduct(int id)
        {
            var product = Products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }

            Products.Remove(product);
            return NoContent();
        }
    }
}
```

Explanation

- **[ApiController] and [Route] Attributes:** These attributes specify that this class is an API controller and define the base route for all actions in the controller.
- **GET /api/products:** Returns a list of all products.
- **GET /api/products/{id}:** Returns a single product by ID.
- **POST /api/products:** Creates a new product.
- **PUT /api/products/{id}:** Updates an existing product.
- **DELETE /api/products/{id}:** Deletes a product by ID.

**Running the API**

To run your API, use the following command:

```
dotnet run
```

Open your browser and navigate to https://localhost:5001/api/products to see the list of products.

## Advanced Features and Best Practices

Now that we have a basic API, let's explore some advanced features and best practices to make it more robust and maintainable.

### Using Entity Framework Core

For real-world applications, you'll typically use a database to store data. Entity Framework Core (EF Core) is an excellent ORM (Object-Relational Mapper) for working with databases in .NET.

**Setting Up EF Core**

First, add EF Core packages to your project:

```
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
```

**Configuring the DbContext**

Create a Data folder and add an AppDbContext.cs file:

```
using Microsoft.EntityFrameworkCore;
using MyApi.Models;

namespace MyApi.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<Product> Products { get; set; }
    }
}
```

**Update Program.cs to configure the DbContext:**

```
using Microsoft.EntityFrameworkCore;
using MyApi.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
```

Add a connection string to appsettings.json:

```
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyApiDb;Trusted_Connection=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
```

**Updating the Products Controller**

Update the ProductsController to use EF Core:

```
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MyApi.Data;
using MyApi.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        private readonly AppDbContext _context;

        public ProductsController(AppDbContext context)
        {
            _context = context;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
        {
            return await _context.Products.ToListAsync();
        }

        [HttpGet("{id}")]
        public async Task<ActionResult<Product>> GetProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }
            return product;
        }

        [HttpPost]
        public async Task<ActionResult<Product>> CreateProduct(Product product)
        {
            _context.Products.Add(product);
            await _context.SaveChangesAsync();

            return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
        }

        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateProduct(int id, Product updatedProduct)
        {
            if (id != updatedProduct.Id)
            {
                return BadRequest();
            }

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

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!_context.Products.Any(e => e.Id == id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }

            _context.Products.Remove(product);
            await _context.SaveChangesAsync();

            return NoContent();
        }
    }
}
```

**Implementing Validation**

To ensure data integrity, it's important to validate input. ASP.NET Core provides built-in validation mechanisms.

Add data annotations to the Product model:

```
using System.ComponentModel.DataAnnotations;

namespace MyApi.Models
{
    public class Product
    {
        public int Id { get; set; }

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

        [Range(0.01, 10000.00)]
        public decimal Price { get; set; }
    }
}
```
Validation errors are automatically handled by ASP.NET Core and returned to the client.

## Conclusion

Building RESTful APIs with ASP.NET Core 8 is a rewarding experience. With all the latest features and improvements, you can make powerful and scalable APIs for todays applications. From setting up your project and defining endpoints to leveraging Entity Framework Core and implementing validation, we've covered the basics to get you started.

And remember, the journey doesn't end here. Continue to learn and enhance your skills and ask questions or even get assistance from the community. Have fun coding!

What challenges have you faced while building APIs? Share your experiences and learn together!

Reach out in case you have questions or require further help. Your feedback and experiences help make this journey a much better one for everybody.

> For those interested in learning more about .NET development, check out our [.NET Development](https://wirefuture.com/blog/dot-net-development) blogs. Stay updated with the latest insights and best practices!