CodeEazy

.NET Core Logo

.NET Core API Made Eazy

1. Introduction & Setup

What is ASP.NET Core?

ASP.NET Core is a free, open-source, cross-platform web framework developed by Microsoft for building modern web applications, APIs, and microservices. It's fast, modular, cloud-optimized, and supports hosting on Windows, macOS, and Linux.

REST API Basics & HTTP Methods

A REST API (Representational State Transfer) provides a uniform interface for interacting with resources using standard HTTP methods:

  • GET – Retrieve data
  • POST – Create new resource
  • PUT – Update entire resource
  • DELETE – Remove a resource

APIs expose endpoints that clients can interact with via URLs, e.g., GET /api/products

Installing .NET SDK and IDE

To build ASP.NET Core apps, install the following:

Verify installation in terminal:

dotnet --version

Creating Your First ASP.NET Core Web API

Open a terminal and run the following commands:

dotnet new webapi -n MyFirstApi
    cd MyFirstApi
    dotnet run

Navigate to https://localhost:5001/swagger to test the default API with Swagger UI.

Project Folder Structure Overview

A typical Web API project contains the following structure:

MyFirstApi/
     ┣ Controllers/
     ┃ ┗ WeatherForecastController.cs
     ┣ Program.cs
     ┣ appsettings.json
     ┗ Startup.cs (for .NET 5 and below)
  • Controllers/ — Contains API controllers
  • Program.cs — Entry point of the application
  • appsettings.json — Configuration (like DB connection strings)

2. Building RESTful Endpoints

Creating Controllers with [ApiController] and [Route]

Controllers handle incoming HTTP requests. Use the [ApiController] attribute to enable API-specific features like automatic model validation and binding.

using Microsoft.AspNetCore.Mvc;
    
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        // Actions go here
    }

Action Methods and Return Types

Action methods process requests and return results using:

  • IActionResult — Flexible return type for various status codes.
  • ActionResult<T> — Combines type safety with HTTP responses.
[HttpGet]
    public IActionResult GetAll()
    {
        return Ok(new[] { "Apple", "Banana" });
    }
    
    [HttpGet("{id}")]
    public ActionResult<string> GetById(int id)
    {
        if (id < 0) return NotFound();
        return "Apple";
    }

Handling Input: Route, Query, and Body Parameters

  • Route parameters: passed directly in the URL
  • Query parameters: optional filters in the query string
  • Body parameters: sent as JSON in POST/PUT requests
[HttpGet("{id}")]
    public IActionResult GetById(int id) => Ok($"ID: {id}");
    
    [HttpGet("search")]
    public IActionResult Search([FromQuery] string name) => Ok($"Search for: {name}");
    
    [HttpPost]
    public IActionResult Create([FromBody] Product product)
    {
        return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
    }

Returning Status Codes

Use built-in methods to return appropriate HTTP status codes:

  • Ok(object) – 200 OK
  • CreatedAtAction – 201 Created
  • BadRequest() – 400 Bad Request
  • NotFound() – 404 Not Found
// Examples:
    return Ok(products);                  // 200
    return CreatedAtAction(...);         // 201
    return BadRequest("Invalid data");   // 400
    return NotFound("Item not found");   // 404

3. Models, Dependency Injection & Services

Creating Model Classes and DTOs

Models represent the core structure of your data. DTOs (Data Transfer Objects) are used to expose limited or modified versions of these models to clients.

// Models/Product.cs
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
// DTOs/ProductDto.cs
    public class ProductDto
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }

Adding a Service Layer with Interfaces

Services encapsulate business logic and data access. Always define an interface for your services to support abstraction and testing.

// Services/IProductService.cs
    public interface IProductService
    {
        IEnumerable<Product> GetAll();
        Product GetById(int id);
        void Add(Product product);
    }
// Services/ProductService.cs
    public class ProductService : IProductService
    {
        private readonly List<Product> _products = new()
        {
            new Product { Id = 1, Name = "Laptop", Price = 1200 },
            new Product { Id = 2, Name = "Phone", Price = 800 }
        };
    
        public IEnumerable<Product> GetAll() => _products;
    
        public Product GetById(int id) => _products.FirstOrDefault(p => p.Id == id);
    
        public void Add(Product product)
        {
            product.Id = _products.Max(p => p.Id) + 1;
            _products.Add(product);
        }
    }

Registering and Injecting Services Using DI

Register your service in the DI container inside Program.cs (or Startup.cs for older versions).

// Program.cs
    builder.Services.AddScoped<IProductService, ProductService>();

Then inject the service into your controller:

// ProductsController.cs
    private readonly IProductService _service;
    
    public ProductsController(IProductService service)
    {
        _service = service;
    }

Using Mock In-Memory Data for Demonstration

For learning and prototyping, it's common to use in-memory lists like the List<Product> in the example above. This avoids the complexity of setting up a database during early development.

4. Data Persistence and EF Core

Installing and Configuring Entity Framework Core

Entity Framework Core (EF Core) is Microsoft’s ORM (Object Relational Mapper) for working with databases using .NET objects.

Install the necessary EF Core packages using NuGet:

// For SQL Server
    dotnet add package Microsoft.EntityFrameworkCore.SqlServer
    dotnet add package Microsoft.EntityFrameworkCore.Tools

Creating DbContext and Model Mappings

The DbContext manages your database connection and maps your models to database tables.

// Data/AppDbContext.cs
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}
    
        public DbSet<Product> Products { get; set; }
    }

Register it in Program.cs:

builder.Services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
    

Define your connection string in appsettings.json:

"ConnectionStrings": {
      "DefaultConnection": "Server=.;Database=CodeEazyDb;Trusted_Connection=True;"
    }

Applying Migrations

Migrations are used to create or update the database schema from your model classes.

// In terminal:
    dotnet ef migrations add InitialCreate
    dotnet ef database update

Performing CRUD Operations using EF Core

You can use LINQ and EF Core APIs to perform database operations.

// Add
    _dbContext.Products.Add(new Product { Name = "Tablet", Price = 500 });
    await _dbContext.SaveChangesAsync();
    
    // Read
    var products = await _dbContext.Products.ToListAsync();
    
    // Update
    var product = await _dbContext.Products.FindAsync(id);
    if (product != null)
    {
        product.Price = 550;
        await _dbContext.SaveChangesAsync();
    }
    
    // Delete
    var toDelete = await _dbContext.Products.FindAsync(id);
    if (toDelete != null)
    {
        _dbContext.Products.Remove(toDelete);
        await _dbContext.SaveChangesAsync();
    }

Async/Await Support in Database Calls

EF Core provides asynchronous methods like ToListAsync(), FindAsync(), and SaveChangesAsync() to improve performance in web applications.

public async Task<IEnumerable<Product>> GetAllProductsAsync()
    {
        return await _dbContext.Products.ToListAsync();
    }

5. Middleware, Security & Deployment

Adding and Using Middleware

Middleware in ASP.NET Core is used to handle requests and responses. Common middleware includes routing, authentication, and static file handling.

// Program.cs
    var app = builder.Build();
    
    app.UseRouting();
    app.UseAuthorization();
    
    app.MapControllers();
    app.Run();

Configuring CORS in Program.cs

CORS (Cross-Origin Resource Sharing) allows your API to be accessible from different domains (like frontend apps hosted elsewhere).

// Add CORS policy
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll",
            builder => builder.AllowAnyOrigin()
                              .AllowAnyMethod()
                              .AllowAnyHeader());
    });
    
    // Use the CORS middleware
    app.UseCors("AllowAll");

Error Handling with UseExceptionHandler

Centralized exception handling prevents exposing raw errors to clients and ensures consistent responses.

// Program.cs
    app.UseExceptionHandler(errorApp =>
    {
        errorApp.Run(async context =>
        {
            context.Response.StatusCode = 500;
            await context.Response.WriteAsync("An unexpected error occurred.");
        });
    });

API Documentation with Swagger

Swagger provides an interactive UI to explore and test your APIs.

// Program.cs
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    
    var app = builder.Build();
    app.UseSwagger();
    app.UseSwaggerUI();

Access Swagger UI at: https://localhost:{port}/swagger

Building and Publishing the API

Use the dotnet publish command to prepare your application for deployment.

dotnet publish -c Release -o ./publish

This will create all necessary files in the publish folder, ready for deployment.

Deployment to IIS

  • Install the .NET Hosting Bundle on the IIS server.
  • Create a new IIS site pointing to the publish folder.
  • Ensure the Application Pool uses No Managed Code.

Deployment to Azure

  • Create an App Service in Azure Portal.
  • Use Visual Studio or GitHub Actions to deploy.
  • Set application settings like ConnectionStrings from the Azure portal.

Deployment with Docker

// Dockerfile
    FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
    WORKDIR /app
    EXPOSE 80
    
    FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
    WORKDIR /src
    COPY . .
    RUN dotnet publish -c Release -o /app/publish
    
    FROM base AS final
    WORKDIR /app
    COPY --from=build /app/publish .
    ENTRYPOINT ["dotnet", "YourAppName.dll"]

Build and run with Docker:

docker build -t codeeazy-api .
    docker run -p 5000:80 codeeazy-api