Docs β€Ί .NET Development β€Ί ASP.NET Core Fundamentals

ASP.NET Core Fundamentals

Introduction to ASP.NET Core for building web APIs and web applications

ASP.NET Core Fundamentals

ASP.NET Core is Microsoft’s modern web framework for building web applications, APIs, and microservices. It’s cross-platform, high-performance, and widely adopted in enterprise environments.

What is ASP.NET Core?

ASP.NET Core is:

  • Cross-platform β€” Runs on Windows, Linux, and macOS
  • High-performance β€” One of the fastest web frameworks available
  • Open-source β€” Developed openly on GitHub
  • Modular β€” Only include what you need

Creating Your First API Project

1. Create the Project

# Create a Web API project
dotnet new webapi -n MyFirstApi
cd MyFirstApi

# Run it
dotnet run

Open your browser to http://localhost:5000/swagger to see the Swagger UI.

2. Project Structure

MyFirstApi/
β”œβ”€β”€ Controllers/
β”‚   └── WeatherForecastController.cs
β”œβ”€β”€ Properties/
β”‚   └── launchSettings.json
β”œβ”€β”€ appsettings.json
β”œβ”€β”€ appsettings.Development.json
β”œβ”€β”€ Program.cs
└── MyFirstApi.csproj

3. Program.cs

In .NET 8, Program.cs is very minimal:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

Controllers and Endpoints

Basic Controller

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    // GET api/products
    [HttpGet]
    public IActionResult GetAll()
    {
        var products = new[]
        {
            new { Id = 1, Name = "Laptop", Price = 999.99 },
            new { Id = 2, Name = "Mouse", Price = 29.99 },
        };
        return Ok(products);
    }
    
    // GET api/products/1
    [HttpGet("{id}")]
    public IActionResult GetById(int id)
    {
        var product = new { Id = id, Name = "Laptop", Price = 999.99 };
        return Ok(product);
    }
    
    // POST api/products
    [HttpPost]
    public IActionResult Create([FromBody] ProductDto product)
    {
        // Save to database
        return CreatedAtAction(nameof(GetById), new { id = 1 }, product);
    }
    
    // PUT api/products/1
    [HttpPut("{id}")]
    public IActionResult Update(int id, [FromBody] ProductDto product)
    {
        // Update in database
        return NoContent();
    }
    
    // DELETE api/products/1
    [HttpDelete("{id}")]
    public IActionResult Delete(int id)
    {
        // Delete from database
        return NoContent();
    }
}

public record ProductDto(string Name, decimal Price);

HTTP Response Codes

return Ok(data);           // 200
return Created(uri, data); // 201
return NoContent();        // 204
return BadRequest();       // 400
return Unauthorized();     // 401
return NotFound();         // 404
return Conflict();         // 409

Dependency Injection

ASP.NET Core has a built-in DI container:

1. Create a Service

// Interface
public interface IProductService
{
    List<Product> GetAll();
    Product? GetById(int id);
    void Add(Product product);
}

// Implementation
public class ProductService : IProductService
{
    private readonly List<Product> _products = new();
    
    public List<Product> GetAll() => _products;
    
    public Product? GetById(int id) => _products.FirstOrDefault(p => p.Id == id);
    
    public void Add(Product product) => _products.Add(product);
}

2. Register in Program.cs

var builder = WebApplication.CreateBuilder(args);

// Register services
builder.Services.AddScoped<IProductService, ProductService>();

// ... rest of configuration

3. Use in a Controller

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    
    // Constructor injection
    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }
    
    [HttpGet]
    public IActionResult GetAll()
    {
        var products = _productService.GetAll();
        return Ok(products);
    }
}

Service Lifetimes

// Transient β€” created every time it's requested
builder.Services.AddTransient<IMyService, MyService>();

// Scoped β€” one instance per HTTP request
builder.Services.AddScoped<IMyService, MyService>();

// Singleton β€” one instance for the entire application
builder.Services.AddSingleton<IMyService, MyService>();

Validation

Model with Data Annotations

using System.ComponentModel.DataAnnotations;

public class CreateProductDto
{
    [Required(ErrorMessage = "Product name is required")]
    [StringLength(100, MinimumLength = 3)]
    public string Name { get; set; }
    
    [Required]
    [Range(0.01, 1000000, ErrorMessage = "Price must be between 0.01 and 1,000,000")]
    public decimal Price { get; set; }
    
    [EmailAddress]
    public string? SupplierEmail { get; set; }
}

Validation in a Controller

[HttpPost]
public IActionResult Create([FromBody] CreateProductDto dto)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    
    // Process...
    return Ok();
}

Configuration

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=MyDb;..."
  },
  "AppSettings": {
    "ApiKey": "secret-key",
    "MaxPageSize": 100
  }
}

Accessing Configuration

// In Program.cs
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");

// Options Pattern
public class AppSettings
{
    public string ApiKey { get; set; }
    public int MaxPageSize { get; set; }
}

builder.Services.Configure<AppSettings>(
    builder.Configuration.GetSection("AppSettings"));

// In a service/controller
public class MyService
{
    private readonly AppSettings _settings;
    
    public MyService(IOptions<AppSettings> options)
    {
        _settings = options.Value;
    }
}

Middleware

Middleware are components that process HTTP requests and responses:

var app = builder.Build();

// Built-in middleware
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

// Custom middleware
app.Use(async (context, next) =>
{
    // Before the request is processed
    Console.WriteLine($"Request: {context.Request.Path}");
    
    await next();
    
    // After the response is created
    Console.WriteLine($"Response: {context.Response.StatusCode}");
});

app.MapControllers();

Minimal APIs (.NET 6+)

A simpler alternative to controllers:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Simple endpoints
app.MapGet("/", () => "Hello World!");

app.MapGet("/products", () =>
{
    return new[] { new { Id = 1, Name = "Laptop" } };
});

app.MapGet("/products/{id}", (int id) =>
{
    return new { Id = id, Name = "Laptop" };
});

app.MapPost("/products", (ProductDto product) =>
{
    return Results.Created($"/products/1", product);
});

app.Run();

record ProductDto(string Name, decimal Price);

Best Practice Tips

  1. Use DTOs β€” Don’t expose database entities directly through your API
  2. Async/Await β€” Use for all I/O operations
  3. Exception Handling β€” Use middleware for global error handling
  4. Logging β€” Use the built-in ILogger
  5. Versioning β€” Implement API versioning from the start

Next Steps

  1. Entity Framework Core β€” Connect to a database
  2. Authentication & Authorization β€” Secure your API
  3. Testing β€” Unit and integration testing

ASP.NET Core is a very powerful framework. Start simple, then increase complexity as needed.