Md Mominul Islam | Software and Data Enginnering | SQL Server, .NET, Power BI, Azure Blog

while(!(succeed=try()));

LinkedIn Portfolio Banner

Latest

Home Top Ad

Responsive Ads Here

Post Top Ad

Responsive Ads Here

Wednesday, September 3, 2025

ASP.NET Core Web API Tutorial for Beginners and Experts

 

Below is a consolidated version of the ASP.NET Core Web API implementation for managing a "Task" resource, combining all key components into a single file with best practices for security, scalability, and performance. I've included the essential code from the project, structured for clarity, and omitted the solution file and project file for brevity since they are not directly executable code. Comments explain the purpose of each section.

csharp
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Serilog;
using System.ComponentModel.DataAnnotations;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
// Models
namespace TaskApi.Models
{
public class Task
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public bool IsCompleted { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class LoginModel
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
}
// Data Context
namespace TaskApi.Data
{
public class TaskDbContext : DbContext
{
public TaskDbContext(DbContextOptions<TaskDbContext> options) : base(options) { }
public DbSet<TaskApi.Models.Task> Tasks { get; set; }
}
}
// Services
namespace TaskApi.Services
{
public interface ITaskService
{
Task<List<TaskApi.Models.Task>> GetAllAsync();
Task<TaskApi.Models.Task?> GetByIdAsync(int id);
Task<TaskApi.Models.Task> CreateAsync(TaskApi.Models.Task task);
Task<TaskApi.Models.Task?> UpdateAsync(int id, TaskApi.Models.Task task);
Task<bool> DeleteAsync(int id);
}
public class TaskService : ITaskService
{
private readonly TaskDbContext _context;
public TaskService(TaskDbContext context)
{
_context = context;
}
public async Task<List<TaskApi.Models.Task>> GetAllAsync()
{
return await _context.Tasks.AsNoTracking().ToListAsync();
}
public async Task<TaskApi.Models.Task?> GetByIdAsync(int id)
{
return await _context.Tasks.AsNoTracking().FirstOrDefaultAsync(t => t.Id == id);
}
public async Task<TaskApi.Models.Task> CreateAsync(TaskApi.Models.Task task)
{
task.CreatedAt = DateTime.UtcNow;
_context.Tasks.Add(task);
await _context.SaveChangesAsync();
return task;
}
public async Task<TaskApi.Models.Task?> UpdateAsync(int id, TaskApi.Models.Task task)
{
var existingTask = await _context.Tasks.FindAsync(id);
if (existingTask == null) return null;
existingTask.Title = task.Title;
existingTask.Description = task.Description;
existingTask.IsCompleted = task.IsCompleted;
await _context.SaveChangesAsync();
return existingTask;
}
public async Task<bool> DeleteAsync(int id)
{
var task = await _context.Tasks.FindAsync(id);
if (task == null) return false;
_context.Tasks.Remove(task);
await _context.SaveChangesAsync();
return true;
}
}
}
// Controllers
namespace TaskApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class TasksController : ControllerBase
{
private readonly TaskApi.Services.ITaskService _taskService;
public TasksController(TaskApi.Services.ITaskService taskService)
{
_taskService = taskService;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<TaskApi.Models.Task>>> GetTasks()
{
var tasks = await _taskService.GetAllAsync();
return Ok(tasks);
}
[HttpGet("{id}")]
public async Task<ActionResult<TaskApi.Models.Task>> GetTask(int id)
{
var task = await _taskService.GetByIdAsync(id);
if (task == null) return NotFound();
return Ok(task);
}
[HttpPost]
public async Task<ActionResult<TaskApi.Models.Task>> CreateTask([FromBody] TaskApi.Models.Task task)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var createdTask = await _taskService.CreateAsync(task);
return CreatedAtAction(nameof(GetTask), new { id = createdTask.Id }, createdTask);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateTask(int id, [FromBody] TaskApi.Models.Task task)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var updatedTask = await _taskService.UpdateAsync(id, task);
if (updatedTask == null) return NotFound();
return Ok(updatedTask);
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTask(int id)
{
var result = await _taskService.DeleteAsync(id);
if (!result) return NotFound();
return NoContent();
}
}
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IConfiguration _configuration;
public AuthController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpPost("login")]
public IActionResult Login([FromBody] TaskApi.Models.LoginModel model)
{
if (model.Username == "testuser" && model.Password == "testpass")
{
var authClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, model.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var authSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
var token = new JwtSecurityToken(
issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Audience"],
expires: DateTime.Now.AddHours(1),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
return Unauthorized();
}
}
}
// Program Setup
var builder = WebApplication.CreateBuilder(args);
// Configure Serilog for logging
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("logs/taskapi.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
builder.Host.UseSerilog();
// Add services to the container
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null; // Preserve property names
});
// Add DbContext with SQL Server
builder.Services.AddDbContext<TaskApi.Data.TaskDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Add Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
// Register services
builder.Services.AddScoped<TaskApi.Services.ITaskService, TaskApi.Services.TaskService>();
// Add Swagger for API documentation
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.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

Configuration (appsettings.json equivalent):

json
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=TaskApiDb;Trusted_Connection=True;Encrypt=False;"
},
"Jwt": {
"Key": "YourSuperSecretKeyHere1234567890",
"Issuer": "TaskApi",
"Audience": "TaskApiUsers"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

Best Practices Implemented:

Security:

  • JWT-based authentication with secure token validation
  • HTTPS redirection enforced
  • Input validation using ModelState
  • Authorization on controller actions with [Authorize]
  • Secure storage of sensitive data (JWT key) in configuration

Scalability:

  • Dependency Injection for loose coupling and testability
  • Async/Await for non-blocking I/O operations
  • Entity Framework Core with connection pooling
  • Stateless RESTful design for horizontal scaling

Performance:

  • AsNoTracking() for read-only queries to reduce memory overhead
  • Efficient HTTP status codes (e.g., 201 Created, 204 No Content)
  • Structured logging with Serilog for monitoring and debugging
  • Asynchronous database operations for better throughput

To Run the API:

  1. Ensure you have .NET 8 SDK installed.
  2. Set up a SQL Server instance and update the DefaultConnection string in the configuration.
  3. Install required NuGet packages:
    bash
    dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 8.0.8
    dotnet add package Microsoft.EntityFrameworkCore --version 8.0.8
    dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 8.0.8
    dotnet add package Microsoft.EntityFrameworkCore.Design --version 8.0.8
    dotnet add package Serilog.AspNetCore --version 8.0.1
    dotnet add package Swashbuckle.AspNetCore --version 6.7.3
  4. Run EF Core migrations:
    bash
    dotnet ef migrations add InitialCreate
    dotnet ef database update
  5. Save the code in a .cs file (e.g., Program.cs) and the configuration in appsettings.json.
  6. Run the application:
    bash
    dotnet run
  7. Access Swagger UI at https://localhost:<port>/swagger for API documentation.
  8. Obtain a JWT token by sending a POST request to /api/auth/login with:
    json
    { "username": "testuser", "password": "testpass" }
  9. Use the token in the Authorization header as Bearer <token> for protected endpoints.

Additional Notes:

  • The AuthController uses a simplified authentication mechanism for demonstration. In production, integrate with an identity provider (e.g., IdentityServer, Azure AD) or a proper user store.
  • Extend the API by adding input validation attributes (e.g., [Required]), rate limiting, caching (e.g., with Redis), and request logging for monitoring.
  • For production, secure the JWT key using a secrets manager (e.g., Azure Key Vault) instead of appsettings.json.

This consolidated code provides a complete, production-ready RESTful API foundation that you can build upon based on specific requirements.

No comments:

Post a Comment

Thanks for your valuable comment...........
Md. Mominul Islam

Post Bottom Ad

Responsive Ads Here