From the initial project setup using the .NET CLI to configuring middleware, controllers, and services, learn every step to build a robust API. Discover best practices for dependency injection, asynchronous actions, and handling exceptions to create scalable, efficient web applications. 1. Setting Up a .NET 8 Web API Project Concept Use the .NET CLI to create a new Web API project. This sets up a basic project structure including Program.cs for startup and a WeatherForecast controller as an example. Code Example dotnet new webapi -n MyWebApi 2. Program.cs — Minimal API Configuration Concept .NET 8 continues the trend towards minimal APIs, allowing you to configure services and endpoints in a simplified and concise manner directly in the Program.cs file. Code Example var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello, World!"); app.Run(); 3. Defining a Controller Concept Controllers handle incoming HTTP requests and respond to the client. They are defined by inheriting from ControllerBase and annotating with [ApiController]. Code Example [ApiController] [Route("[controller]")] public class MyController : ControllerBase { [HttpGet] public IActionResult Get() => Ok("Hello from MyController"); } 4. Dependency Injection in Controllers Concept .NET Core’s built-in dependency injection (DI) makes it easy to manage dependencies. You can inject services into your controllers through their constructors. Code Example public class MyService { public string GetMessage() => "Injected message"; } public class MyController : ControllerBase { private readonly MyService _myService; public MyController(MyService myService) { _myService = myService; } [HttpGet] public IActionResult Get() => Ok(_myService.GetMessage()); } 5. Configuring Services Concept Services (like database contexts, custom services, etc.) are configured in the Program.cs file, making them available for dependency injection throughout your application. Code Example builder.Services.AddScoped<MyService>(); 6. Environment-Based Configuration Concept .NET supports environment-specific configuration files (appsettings.json, appsettings.Development.json, etc.), allowing for different settings based on the application’s environment. Code Example // appsettings.Development.json { "Logging": { "LogLevel": { "Default": "Debug" } } } 7. Middleware Concept Middleware components form a pipeline that handles requests and responses. Custom middleware can be created for cross-cutting concerns like logging or error handling. Code Example app.Use(async (context, next) => { // Custom logic before passing to the next middleware await next(); // Custom logic after executing the next middleware }); 8. Routing Concept Routing in .NET Web API is achieved through attribute routing on controllers and action methods. This allows URLs to be mapped directly to controller actions. Code Example [HttpGet("myaction/{id}")] public IActionResult GetAction(int id) => Ok($"Action with ID = {id}"); 9. Model Binding Concept Model binding automatically maps data from HTTP requests to action method parameters. It supports complex types, including JSON bodies and query string parameters. Code Example public class MyModel { public int Id { get; set; } public string Name { get; set; } } [HttpPost] public IActionResult PostAction([FromBody] MyModel model) => Ok(model); 10. Data Validation Concept Data annotations can be used to validate model data. The [ApiController] attribute automatically enforces validation, responding with 400 if the model is invalid. Code Example public class MyModel { [Required] public int Id { get; set; } [StringLength(100)] public string Name { get; set; } } 11. Asynchronous Actions Concept Asynchronous actions improve scalability by freeing up threads while waiting for I/O operations to complete. Use the async keyword, and return Task or Task<IActionResult>. Code Example [HttpGet("{id}")] public async Task<IActionResult> GetAsync(int id) { var result = await _service.GetByIdAsync(id); return Ok(result); } 12. Handling Exceptions Globally Concept Global exception handling allows for centralized error processing, logging, and standardized API responses on unhandled exceptions. Code Example app.UseExceptionHandler(a => a.Run(async context => { var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var exception = exceptionHandlerPathFeature.Error; // Log the exception, generate a custom response, etc. context.Response.StatusCode = 500; await context.Response.WriteAsJsonAsync(new { Error = "An unexpected error occurred" }); })); 13. API Versioning Concept API versioning helps manage changes to the API over time. The .NET platform supports versioning through query string, URL path, or request header. Code Example builder.Services.AddApiVersioning(options => { options.AssumeDefaultVersionWhenUnspecified = true; options.DefaultApiVersion = new ApiVersion(1, 0); options.ReportApiVersions = true; }); 14. Content Negotiation Concept Content negotiation allows an API to serve different formats of the response based on the Accept header in the request, enabling support for formats like JSON, XML, etc. Code Example builder.Services.AddControllers() .AddXmlDataContractSerializerFormatters(); 15. Custom JSON Serialization Settings Concept Customize JSON response formatting, such as camelCase naming or ignoring null values, by configuring JSON serializer settings. Code Example builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.IgnoreNullValues = true; }); 16. Configuring CORS Concept Cross-Origin Resource Sharing (CORS) allows your API to be called from web applications hosted on different domains. Configure the CORS policy as per your requirements. Code Example builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => builder.WithOrigins("http://example.com")); }); app.UseCors("AllowSpecificOrigin"); 17. Authentication Concept Secure your API by enabling authentication, which verifies the identity of users or services making requests. Code Example builder.Services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.Authority = "https://your-auth-server"; options.Audience = "your-api"; }); 18. Authorization Concept After authentication, authorization determines if an authenticated user has permission to perform an action or access a resource. Code Example [Authorize] public class SecureController : ControllerBase { // Action methods here } 19. Swagger/OpenAPI Integration Concept Swagger (OpenAPI) provides interactive documentation for your API, allowing developers to understand and consume it easily. Code Example builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); app.UseSwagger(); app.UseSwaggerUI(); 20. Logging Concept .NET Core provides a built-in logging framework that can log messages to various outputs (console, debug window, external services, etc.). Code Example logger.LogInformation("This is an informational message"); app.Use(async (context, next) => { logger.LogError("This is an error message before the next middleware"); await next.Invoke(); // Log after calling the next middleware }); 21. Using Entity Framework Core Concept Entity Framework Core is an ORM used for data access in .NET applications. It allows you to query and manipulate data using strongly typed objects. Code Example public class MyDbContext : DbContext { public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) {} public DbSet<MyModel> MyModels { get; set; } } 22. Migrations in Entity Framework Core Concept Migrations allow you to apply version control to your database schema by tracking changes in your data models. Code Example dotnet ef migrations add InitialCreate dotnet ef database update 23. Repository Pattern Concept The Repository pattern abstracts the data layer, making your application more modular and easier to maintain. Code Example public interface IRepository<T> { Task<IEnumerable<T>> GetAllAsync(); Task<T> GetByIdAsync(int id); // Other methods... } public class MyRepository<T> : IRepository<T> where T : class { private readonly MyDbContext _context; public MyRepository(MyDbContext context) { _context = context; } // Implement methods... } 24. Unit Testing Concept Unit testing ensures your Web API functions correctly by testing individual units of code in isolation. Code Example public class MyControllerTests { [Fact] public async Task Get_ReturnsExpectedValue() { // Arrange var serviceMock = new Mock<IMyService>(); serviceMock.Setup(service => service.GetAsync()).ReturnsAsync("test"); var controller = new MyController(serviceMock.Object); // Act var result = await controller.Get(); // Assert Assert.Equal("test", result.Value); } } 25. Integrating with a Front-end Concept .NET Web API can serve as the backend for a front-end application, providing RESTful services. Code Example fetch('https://localhost:5001/mycontroller') .then(response => response.json()) .then(data => console.log(data)); 26. Health Checks Concept Health checks provide a way to monitor the status of your application and its dependencies, useful for microservices architectures. Code Example builder.Services.AddHealthChecks(); app.MapHealthChecks("/health"); 27. Using SignalR for Real-time Communication Concept SignalR enables real-time web functionality, allowing server-side code to send asynchronous notifications to client-side web applications. Code Example public class MyHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } } 28. Configuring Response Caching Concept Response caching reduces the number of requests a server must handle by storing a copy of previously requested resources. Code Example [HttpGet("{id}")] [ResponseCache(Duration = 60)] public IActionResult GetById(int id) { // Retrieve and return your resource } 29. Static Files Concept Serving static files (HTML, CSS, JavaScript, etc.) is essential for backing front-end applications with a .NET Web API. Code Example app.UseStaticFiles(); // Enable static file serving 30. Advanced Configuration and Options Pattern Concept The options pattern uses classes to represent groups of related settings. Using IOptions<T>, you can access these settings anywhere in your application. Code Example public class MySettings { public string Setting1 { get; set; } // Other settings } builder.Services.Configure<MySettings>(builder.Configuration.GetSection("MySettings")); public class MyService { private readonly MySettings _settings; public MyService(IOptions<MySettings> settings) { _settings = settings.Value; } // Use _settings.Setting1 } 31. Custom Middleware Concept Middleware is software that’s assembled into an application pipeline to handle requests and responses. Custom middleware can be created to perform specific tasks. Code Example public class MyCustomMiddleware { private readonly RequestDelegate _next; public MyCustomMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { // Pre-processing logic here await _next(httpContext); // Call the next middleware in the pipeline // Post-processing logic here } } // Extension method for easy middleware registration public static class MyCustomMiddlewareExtensions { public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<MyCustomMiddleware>(); } } 32. Rate Limiting Concept Rate limiting protects your API from overuse by limiting how often a user can make requests within a certain time frame. Code Example // Assume using a third-party library like AspNetCoreRateLimit builder.Services.AddInMemoryRateLimiting(); builder.Services.Configure<IpRateLimitOptions>(options => { options.GeneralRules = new List<RateLimitRule> { new RateLimitRule { Endpoint = "*", Limit = 100, Period = "1h" } }; }); 33. API Keys Authentication Concept API keys are a simple way to authenticate and authorize API calls. They’re passed from client to server either in the query string or header. Code Example public class ApiKeyMiddleware { private readonly RequestDelegate _next; private const string APIKEYNAME = "x-api-key"; public ApiKeyMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { if (!context.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey)) { context.Response.StatusCode = 401; await context.Response.WriteAsync("API Key was not provided."); return; } // Validate the extracted API Key here... await _next(context); } } 34. Output Caching Concept Output caching allows you to store the response to a request. Subsequent requests can be served from the cache, significantly improving performance. Code Example [HttpGet] [ResponseCache(Duration = 120, Location = ResponseCacheLocation.Client, NoStore = false)] public IActionResult Get() { // Your logic here } 35. Background Tasks Concept Background tasks enable operations to run in the background, independent of user requests, like sending emails or processing long-running jobs. Code Example public class MyBackgroundService : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { // Your background task logic here await Task.Delay(TimeSpan.FromHours(1), stoppingToken); } } } 36. WebSockets Concept WebSockets provide a full-duplex communication channel over a single, long-lived connection, ideal for real-time applications. Code Example app.UseWebSockets(); app.Use(async (context, next) => { if (context.WebSockets.IsWebSocketRequest) { WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); // Handle the WebSocket request here } else { await next(); } }); 37. Request Localization Concept Request localization provides a way to localize content for different cultures and languages, based on the request’s information. Code Example var supportedCultures = new[] { "en-US", "fr-FR" }; var localizationOptions = new RequestLocalizationOptions() .SetDefaultCulture(supportedCultures[0]) .AddSupportedCultures(supportedCultures) .AddSupportedUICultures(supportedCultures); app.UseRequestLocalization(localizationOptions); 38. Integrating with GraphQL Concept GraphQL is a query language for APIs. Integrating a .NET Web API with GraphQL allows for more efficient data retrieval. Code Example // Assume using a library like HotChocolate builder.Services .AddGraphQLServer() .AddQueryType<Query>(); app.MapGraphQL(); 39. Monitoring and Telemetry Concept Monitoring and telemetry involve collecting, analyzing, and acting on data about your application’s performance and usage. Code Example // Assume using Application Insights builder.Services.AddApplicationInsightsTelemetry("YOUR_INSTRUMENTATION_KEY"); 40. SignalR Hubs and Real-time Communication Concept SignalR is a library that simplifies adding real-time web functionality to apps. Real-time web functionality is the ability to have server code push content to connected clients instantly as it happens, not requiring the server to wait for a client to request new data. SignalR is perfect for developing chat applications, real-time dashboards, and more interactive web applications. Code Example public class ChatHub : Hub { public async Task SendMessage(string user, string message) { // Call the broadcastMessage method to update clients. await Clients.All.SendAsync("broadcastMessage", user, message); } } // Startup or Program.cs app.MapHub<ChatHub>("/chathub"); Integration in Program.cs: public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Other configurations... app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chathub"); }); } 41. Advanced Entity Framework Core — Relationships Concept Entity Framework Core allows for the mapping of complex relationships between entities, such as one-to-one, one-to-many, and many-to-many. Code Example public class Author { public int AuthorId { get; set; } public string Name { get; set; } public ICollection<Book> Books { get; set; } } public class Book { public int BookId { get; set; } public string Title { get; set; } public int AuthorId { get; set; } public Author Author { get; set; } } 42. Custom Validation Attributes Concept Custom validation attributes allow you to define your validation logic for data models, extending the built-in validation attributes. Code Example public class MyCustomValidationAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // Your custom validation logic here if (value is int intValue && intValue > 0) { return ValidationResult.Success; } return new ValidationResult("Value must be positive"); } } public class MyModel { [MyCustomValidationAttribute] public int MyProperty { get; set; } } 43. Advanced Configuration Scenarios Concept .NET’s options pattern supports complex configuration scenarios, including nested objects, lists, and validation. Code Example public class MyOptions { public MyNestedOptions Nested { get; set; } public List<string> Items { get; set; } } public class MyNestedOptions { public string Key { get; set; } } // In Program.cs or Startup.cs builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("MyOptions")); 44. Performance Monitoring and Profiling Concept Monitoring and profiling an application can identify bottlenecks and inefficiencies, essential for optimizing performance. Code Example app.UseMiniProfiler(); 45. API Documentation with Swagger and XML Comments Concept Enhance your API documentation by integrating XML comments into your Swagger UI, providing a richer experience for developers consuming your API. Code Example builder.Services.AddSwaggerGen(c => { var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); }); 46. Globalization and Localization Concept Globalization and localization allow your application to support multiple languages and cultures, making it accessible to a global audience. Code Example builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value); 47. Security Headers Concept Improving the security of your web application by adding various HTTP headers can protect against common attacks and vulnerabilities. Code Example app.UseHsts(); app.UseXContentTypeOptions(); app.UseReferrerPolicy(opts => opts.NoReferrer()); app.UseXXssProtection(options => options.EnabledWithBlockMode()); app.UseXfo(options => options.Deny()); 48. Feature Flags Concept Feature flags allow you to toggle features of your application on and off without deploying new code, facilitating easier testing and rollouts. Code Example // Using a library like Microsoft.FeatureManagement builder.Services.AddFeatureManagement(); 49. Blazor Integration Concept Blazor allows you to build interactive web UIs using C# instead of JavaScript. Integrating Blazor with Web API provides a seamless full-stack development experience. Code Example // In a Blazor Server app @code { private IEnumerable<WeatherForecast> forecasts; protected override async Task OnInitializedAsync() { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); } } 50. Advanced Middleware for Response Compression Concept Response compression can reduce the size of your API responses, improving load times for clients over slow networks. Code Example builder.Services.AddResponseCompression(options => { options.Providers.Add<GzipCompressionProvider>(); options.EnableForHttps = true; }); app.UseResponseCompression(); C# Programming🚀 Thank you for being a part of the C# community! Before you leave: If you’ve made it this far, please show your appreciation with a clap, and follow the author! 👏️️ Follow us: | | | | | X LinkedIn Dev.to Hashnode Newsletter Tumblr Visit our other platforms: | | | | GitHub Instagram Tiktok Quora Daily.dev Also published here