starter ASP.NET Core Web API project with example

Starter ASP.NET Core Web API project that follows all the best practices listed above.


🛠️ Starter Project Overview

We’ll build a Product Catalog API with the following features:

🔧 Tech Stack:

  • ASP.NET Core 8

  • Entity Framework Core (In-Memory for now)

  • DTOs and Services

  • Validation

  • API Versioning

  • Swagger (OpenAPI)

  • Global Exception Handling

  • Logging

  • Async support

  • Dependency Injection

  • Clean architecture (Controllers → Services → Repository)


📁 Project Structure:

mathematica

ProductCatalogApi/ ├── Controllers/ │ └── v1/ │ └── ProductsController.cs ├── Dtos/ │ └── ProductDto.cs ├── Entities/ │ └── Product.cs ├── Interfaces/ │ └── IProductService.cs │ └── IProductRepository.cs ├── Repositories/ │ └── ProductRepository.cs ├── Services/ │ └── ProductService.cs ├── Middlewares/ │ └── ExceptionMiddleware.cs ├── Data/ │ └── ApplicationDbContext.cs ├── Program.cs └── ProductCatalogApi.csproj

✅ 1. Initialize Project

Create the project:

bash

dotnet new webapi -n ProductCatalogApi cd ProductCatalogApi

Enable nullable and implicit usings in csproj (optional):

xml

<Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings>

✅ 2. Create Product Entity

Entities/Product.cs

csharp

namespace ProductCatalogApi.Entities; public class Product { public int Id { get; set; } public string Name { get; set; } = string.Empty; public decimal Price { get; set; } }

✅ 3. Create DTO

Dtos/ProductDto.cs

csharp

using System.ComponentModel.DataAnnotations; namespace ProductCatalogApi.Dtos; public class ProductDto { public int Id { get; set; } [Required] [StringLength(100)] public string Name { get; set; } = string.Empty; [Range(0.01, 10000)] public decimal Price { get; set; } }

✅ 4. ApplicationDbContext

Data/ApplicationDbContext.cs

csharp

using Microsoft.EntityFrameworkCore; using ProductCatalogApi.Entities; namespace ProductCatalogApi.Data; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Product> Products => Set<Product>(); }

✅ 5. Repository

Interfaces/IProductRepository.cs

csharp

using ProductCatalogApi.Entities; namespace ProductCatalogApi.Interfaces; public interface IProductRepository { Task<IEnumerable<Product>> GetAllAsync(); Task<Product?> GetByIdAsync(int id); Task<Product> AddAsync(Product product); Task UpdateAsync(Product product); Task DeleteAsync(Product product); }

Repositories/ProductRepository.cs

csharp

using Microsoft.EntityFrameworkCore; using ProductCatalogApi.Data; using ProductCatalogApi.Entities; using ProductCatalogApi.Interfaces; namespace ProductCatalogApi.Repositories; public class ProductRepository : IProductRepository { private readonly ApplicationDbContext _context; public ProductRepository(ApplicationDbContext context) { _context = context; } public async Task<IEnumerable<Product>> GetAllAsync() => await _context.Products.ToListAsync(); public async Task<Product?> GetByIdAsync(int id) => await _context.Products.FindAsync(id); public async Task<Product> AddAsync(Product product) { _context.Products.Add(product); await _context.SaveChangesAsync(); return product; } public async Task UpdateAsync(Product product) { _context.Products.Update(product); await _context.SaveChangesAsync(); } public async Task DeleteAsync(Product product) { _context.Products.Remove(product); await _context.SaveChangesAsync(); } }

✅ 6. Service Layer

Interfaces/IProductService.cs

csharp

using ProductCatalogApi.Dtos; namespace ProductCatalogApi.Interfaces; public interface IProductService { Task<IEnumerable<ProductDto>> GetAllAsync(); Task<ProductDto?> GetByIdAsync(int id); Task<ProductDto> CreateAsync(ProductDto dto); Task<bool> UpdateAsync(int id, ProductDto dto); Task<bool> DeleteAsync(int id); }

Services/ProductService.cs

csharp

using ProductCatalogApi.Dtos; using ProductCatalogApi.Entities; using ProductCatalogApi.Interfaces; namespace ProductCatalogApi.Services; public class ProductService : IProductService { private readonly IProductRepository _repo; public ProductService(IProductRepository repo) { _repo = repo; } public async Task<IEnumerable<ProductDto>> GetAllAsync() { var products = await _repo.GetAllAsync(); return products.Select(p => new ProductDto { Id = p.Id, Name = p.Name, Price = p.Price }); } public async Task<ProductDto?> GetByIdAsync(int id) { var product = await _repo.GetByIdAsync(id); if (product == null) return null; return new ProductDto { Id = product.Id, Name = product.Name, Price = product.Price }; } public async Task<ProductDto> CreateAsync(ProductDto dto) { var product = new Product { Name = dto.Name, Price = dto.Price }; var result = await _repo.AddAsync(product); return new ProductDto { Id = result.Id, Name = result.Name, Price = result.Price }; } public async Task<bool> UpdateAsync(int id, ProductDto dto) { var existing = await _repo.GetByIdAsync(id); if (existing == null) return false; existing.Name = dto.Name; existing.Price = dto.Price; await _repo.UpdateAsync(existing); return true; } public async Task<bool> DeleteAsync(int id) { var existing = await _repo.GetByIdAsync(id); if (existing == null) return false; await _repo.DeleteAsync(existing); return true; } }

✅ 7. Controller

Controllers/v1/ProductsController.cs

csharp

using Microsoft.AspNetCore.Mvc; using ProductCatalogApi.Dtos; using ProductCatalogApi.Interfaces; namespace ProductCatalogApi.Controllers.v1; [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] public class ProductsController : ControllerBase { private readonly IProductService _service; public ProductsController(IProductService service) { _service = service; } [HttpGet] public async Task<IActionResult> GetAll() => Ok(await _service.GetAllAsync()); [HttpGet("{id}")] public async Task<IActionResult> Get(int id) { var product = await _service.GetByIdAsync(id); if (product == null) return NotFound(); return Ok(product); } [HttpPost] public async Task<IActionResult> Create(ProductDto dto) { if (!ModelState.IsValid) return BadRequest(ModelState); var product = await _service.CreateAsync(dto); return CreatedAtAction(nameof(Get), new { id = product.Id }, product); } [HttpPut("{id}")] public async Task<IActionResult> Update(int id, ProductDto dto) { if (!ModelState.IsValid) return BadRequest(ModelState); var updated = await _service.UpdateAsync(id, dto); return updated ? NoContent() : NotFound(); } [HttpDelete("{id}")] public async Task<IActionResult> Delete(int id) { var deleted = await _service.DeleteAsync(id); return deleted ? NoContent() : NotFound(); } }

✅ 8. Middleware for Error Handling

Middlewares/ExceptionMiddleware.cs

csharp

namespace ProductCatalogApi.Middlewares; public class ExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILogger<ExceptionMiddleware> _logger; public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception ex) { _logger.LogError(ex, "Unhandled Exception"); context.Response.StatusCode = 500; await context.Response.WriteAsJsonAsync(new { error = "An unexpected error occurred." }); } } }

✅ 9. Configure Program.cs

Program.cs

csharp

using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using ProductCatalogApi.Data; using ProductCatalogApi.Interfaces; using ProductCatalogApi.Middlewares; using ProductCatalogApi.Repositories; using ProductCatalogApi.Services; var builder = WebApplication.CreateBuilder(args); // DB builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase("ProductDb")); // Services builder.Services.AddScoped<IProductService, ProductService>(); builder.Services.AddScoped<IProductRepository, ProductRepository>(); // Swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // Versioning builder.Services.AddApiVersioning(options => { options.DefaultApiVersion = new ApiVersion(1, 0); options.AssumeDefaultVersionWhenUnspecified = true; options.ReportApiVersions = true; }); // Controllers builder.Services.AddControllers(); var app = builder.Build(); // Middlewares app.UseMiddleware<ExceptionMiddleware>(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();

✅ 10. Run the App


dotnet run

Visit:

  • Swagger: https://localhost:{port}/swagger

  • Try endpoints like:

    • GET /api/v1/products

    • POST /api/v1/products


No comments:

Post a Comment

starter ASP.NET Core Web API project with example

Starter ASP.NET Core Web API project that follows all the best practices listed above. 🛠️ Starter Project Overview We’ll build a Produc...

Best for you