
.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:
- .NET SDK
- Visual Studio (Community Edition) OR
- Visual Studio Code (with C# extension)
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 controllersProgram.cs
— Entry point of the applicationappsettings.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 OKCreatedAtAction
– 201 CreatedBadRequest()
– 400 Bad RequestNotFound()
– 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