Creating a comprehensive .NET Core Web API for a library service involves several key components and best practices. Below is a detailed guide to achieve this, covering multiple aspects such as versioning, routing, exception handling, monitoring, health checks, load balancing, authentication, and authorization.
Step-by-Step Implementation
Project Setup
Create a new .NET Core Web API project:
bash
=====================================
dotnet new webapi -n LibraryService
cd LibraryService
Project Structure
Organize your project using the following structure:
=====================================
LibraryService
├── Controllers
├── Models
├── Data
├── Services
├── DTOs
├── Middleware
├── Extensions
├── Policies
└── Program.cs
Versioning and Routing
Install the necessary packages:
bash
=====================================
dotnet add package Microsoft.AspNetCore.Mvc.Versioning
dotnet add package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
Configure versioning in Program.cs:
csharp
=====================================
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ReportApiVersions = true;
});
builder.Services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
var app = builder.Build();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
Controllers and Endpoints
Create a basic controller in Controllers/BooksController.cs:
csharp
=====================================
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class BooksController : ControllerBase
{
[HttpGet]
public IActionResult GetBooks()
{
return Ok(new[] { "Book1", "Book2" });
}
}
Exception Handling
Create a custom middleware for exception handling:
csharp
=====================================
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new { error = ex.Message }.ToString());
}
}
Register middleware in Program.cs:
csharp
=====================================
app.UseMiddleware<ExceptionHandlingMiddleware>();
Monitoring and Health Checks
Install health check packages:
bash
=====================================
dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks
dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
Configure health checks in Program.cs:
csharp
=====================================
builder.Services.AddHealthChecks();
builder.Services.AddHealthChecksUI().AddInMemoryStorage();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
endpoints.MapHealthChecksUI();
});
Load Balancing
Use a reverse proxy like Nginx or a load balancer service such as Azure Load Balancer or AWS Elastic Load Balancer. Configure your load balancer to distribute requests across multiple instances of your API service.
Authentication and Authorization
Install authentication and authorization packages:
bash
=====================================
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Authorization
Configure authentication in Program.cs:
csharp
=====================================
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your-issuer",
ValidAudience = "your-audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"))
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Admin"));
options.AddPolicy("UserPolicy", policy => policy.RequireRole("User"));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
Define Policies and Roles
Apply policies and roles in controllers:
csharp
=====================================
[Authorize(Policy = "AdminPolicy")]
[HttpPost]
public IActionResult AddBook([FromBody] BookDto book)
{
// Add book logic
return Ok();
}
Extensions and Utilities
Create extension methods for better organization. For example, create Extensions/ServiceExtensions.cs to add all the service configurations:
csharp
=====================================
public static class ServiceExtensions
{
public static void ConfigureCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder =>
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
}
public static void ConfigureIISIntegration(this IServiceCollection services)
{
services.Configure<IISOptions>(options => { });
}
public static void ConfigureLoggerService(this IServiceCollection services)
{
services.AddSingleton<ILoggerManager, LoggerManager>();
}
}
DTOs and Models
Create Data Transfer Objects (DTOs) and models for your entities. For example, Models/Book.cs and DTOs/BookDto.cs:
csharp
=====================================
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
}
public class BookDto
{
public string Title { get; set; }
public string Author { get; set; }
}
Final Project Setup
Your Program.cs should look similar to this after integrating all the components:
csharp
=====================================
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ReportApiVersions = true;
});
builder.Services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
builder.Services.ConfigureCors();
builder.Services.ConfigureIISIntegration();
builder.Services.ConfigureLoggerService();
builder.Services.AddHealthChecks();
builder.Services.AddHealthChecksUI().AddInMemoryStorage();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your-issuer",
ValidAudience = "your-audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"))
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Admin"));
options.AddPolicy("UserPolicy", policy => policy.RequireRole("User"));
});
var app = builder.Build();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
endpoints.MapHealthChecksUI();
});
app.Run();
This setup provides a comprehensive framework for a robust, secure, and scalable library service API in .NET Core. You can further enhance and customize each component according to your specific requirements.
=========================================================================
Explanation
========================================================================
Let's go through each section of the code step by step to understand what it does and why it's important.
1. Project Setup
bash
=====================================
dotnet new webapi -n LibraryService
cd LibraryService
This command creates a new .NET Core Web API project named LibraryService and navigates into the project directory.
2. Versioning and Routing
Adding Packages
bash
=====================================
dotnet add package Microsoft.AspNetCore.Mvc.Versioning
dotnet add package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
These packages are used to enable API versioning and versioned API exploration.
Configuring Versioning in Program.cs
csharp
=====================================
builder.Services.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ReportApiVersions = true;
});
builder.Services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
AssumeDefaultVersionWhenUnspecified: If the API version is not specified in the request, the default version will be assumed.
DefaultApiVersion: Sets the default API version to 1.0.
ReportApiVersions: Adds the API versions supported by this API to the response headers.
GroupNameFormat: Specifies the format of the versioned API group names.
SubstituteApiVersionInUrl: Indicates whether the API version should be substituted in the URL.
3. Controllers and Endpoints
Creating a Basic Controller
csharp
=====================================
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class BooksController : ControllerBase
{
[HttpGet]
public IActionResult GetBooks()
{
return Ok(new[] { "Book1", "Book2" });
}
}
ApiController: Indicates that this class is an API controller.
Route("api/v{version:apiVersion}/[controller]"): Defines the route template for the API, including versioning.
ApiVersion("1.0"): Specifies that this controller is for version 1.0 of the API.
GetBooks(): A simple action method that returns a list of books.
4. Exception Handling
Creating Custom Middleware
csharp
=====================================
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new { error = ex.Message }.ToString());
}
}
This middleware handles exceptions globally:
It catches any exception that occurs during the processing of HTTP requests.
It sets the response status code to 500 (Internal Server Error) and returns a JSON response with the error message.
Registering Middleware
csharp
=====================================
app.UseMiddleware<ExceptionHandlingMiddleware>();
Registers the custom exception handling middleware in the request pipeline.
5. Monitoring and Health Checks
Adding Health Check Packages
bash
=====================================
dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks
dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
These packages provide health check capabilities and a UI to monitor the health status.
Configuring Health Checks
csharp
=====================================
builder.Services.AddHealthChecks();
builder.Services.AddHealthChecksUI().AddInMemoryStorage();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
endpoints.MapHealthChecksUI();
});
AddHealthChecks(): Registers the health check services.
AddHealthChecksUI().AddInMemoryStorage(): Registers the health checks UI and uses in-memory storage to persist health check results.
MapHealthChecks("/health"): Maps the health check endpoint to /health.
MapHealthChecksUI(): Maps the health check UI endpoint.
6. Authentication and Authorization
Adding Packages
bash
=====================================
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Authorization
These packages enable JWT (JSON Web Token) authentication and authorization.
Configuring Authentication and Authorization
csharp
=====================================
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your-issuer",
ValidAudience = "your-audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"))
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Admin"));
options.AddPolicy("UserPolicy", policy => policy.RequireRole("User"));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
Configures JWT authentication with token validation parameters.
Adds authorization policies that require specific roles.
7. Applying Policies and Roles in Controllers
csharp
=====================================
[Authorize(Policy = "AdminPolicy")]
[HttpPost]
public IActionResult AddBook([FromBody] BookDto book)
{
// Add book logic
return Ok();
}
Authorize(Policy = "AdminPolicy"): Restricts access to the AddBook action to users with the Admin role.
8. Extensions and Utilities
Creating Extension Methods
csharp
=====================================
public static class ServiceExtensions
{
public static void ConfigureCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder =>
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
}
public static void ConfigureIISIntegration(this IServiceCollection services)
{
services.Configure<IISOptions>(options => { });
}
public static void ConfigureLoggerService(this IServiceCollection services)
{
services.AddSingleton<ILoggerManager, LoggerManager>();
}
}
ConfigureCors(): Configures CORS (Cross-Origin Resource Sharing) policy.
ConfigureIISIntegration(): Configures IIS integration options.
ConfigureLoggerService(): Configures a singleton service for logging.
9. DTOs and Models
Creating Data Transfer Objects (DTOs) and Models
csharp
=====================================
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
}
public class BookDto
{
public string Title { get; set; }
public string Author { get; set; }
}
Book: Represents the book entity.
BookDto: Represents the data transfer object for the book.
Final Project Setup in Program.cs
csharp
=====================================
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ReportApiVersions = true;
});
builder.Services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
builder.Services.ConfigureCors();
builder.Services.ConfigureIISIntegration();
builder.Services.ConfigureLoggerService();
builder.Services.AddHealthChecks();
builder.Services.AddHealthChecksUI().AddInMemoryStorage();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your-issuer",
ValidAudience = "your-audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"))
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Admin"));
options.AddPolicy("UserPolicy", policy => policy.RequireRole("User"));
});
var app = builder.Build();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
endpoints.MapHealthChecksUI();
});
app.Run();
This configuration sets up the .NET Core Web API with all necessary services, middleware, and endpoint mappings for a comprehensive library service, ensuring it is secure, robust, and scalable