ASP.NET Core Quick Reference Cheatsheet
Request Pipeline
HTTP Request → Middleware 1 → Middleware 2 → ... → Routing → Endpoint
HTTP Response ← Middleware 1 ← Middleware 2 ← ... ← Routing ← Endpoint
Middleware Order (Critical!)
app.UseExceptionHandler("/Error"); // 1. Exception handling (first)
app.UseHsts(); // 2. HSTS
app.UseHttpsRedirection(); // 3. HTTPS redirect
app.UseStaticFiles(); // 4. Static files (short-circuit)
app.UseRouting(); // 5. Routing
app.UseCors(); // 6. CORS
app.UseAuthentication(); // 7. Authentication
app.UseAuthorization(); // 8. Authorization
app.UseEndpoints(...); // 9. Endpoints (last)
Custom Middleware
app.Use(async (context, next) =>
{
// Before next middleware
await next();
// After next middleware
});
app.Run(async context => // Terminal middleware
{
await context.Response.WriteAsync("Hello");
});
Dependency Injection
Service Lifetimes
| Lifetime | Description | Use For |
|---|---|---|
Singleton |
One instance for app lifetime | Stateless services, caches |
Scoped |
One instance per request | DbContext, repositories |
Transient |
New instance every time | Lightweight, stateless |
Registration Patterns
// Basic
services.AddSingleton<IService, Service>();
services.AddScoped<IService, Service>();
services.AddTransient<IService, Service>();
// Factory
services.AddScoped<IService>(sp => new Service(sp.GetRequiredService<IDep>()));
// Multiple implementations
services.AddScoped<IService, ServiceA>();
services.AddScoped<IService, ServiceB>();
// IEnumerable<IService> injects both
Scoped Service in Singleton (Safe Pattern)
public class MySingleton
{
private readonly IServiceScopeFactory _scopeFactory;
public async Task DoWork()
{
using var scope = _scopeFactory.CreateScope();
var scoped = scope.ServiceProvider.GetRequiredService<IScopedService>();
await scoped.ProcessAsync();
}
}
Configuration
Precedence Order (Last Wins)
appsettings.jsonappsettings.{Environment}.json- User Secrets (Development)
- Environment Variables
- Command Line Arguments
Options Pattern
// Registration
services.Configure<MyOptions>(config.GetSection("MyOptions"));
// Usage comparison
IOptions<T> // Singleton, read once at startup
IOptionsSnapshot<T> // Scoped, re-read per request
IOptionsMonitor<T> // Singleton, supports change notifications
// Validation
services.AddOptions<MyOptions>()
.Bind(config.GetSection("MyOptions"))
.ValidateDataAnnotations()
.ValidateOnStart();
Authentication & Authorization
JWT Setup (Quick)
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(key)
};
});
Policy-Based Authorization
// Define
services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("Premium", policy => policy.RequireClaim("Subscription", "Premium"));
});
// Use
[Authorize(Policy = "AdminOnly")]
public IActionResult AdminPage() { }
Custom Authorization Handler
public class MinAgeRequirement : IAuthorizationRequirement
{
public int MinAge { get; }
public MinAgeRequirement(int age) => MinAge = age;
}
public class MinAgeHandler : AuthorizationHandler<MinAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinAgeRequirement requirement)
{
var birthDate = context.User.FindFirst("BirthDate")?.Value;
if (birthDate != null && CalculateAge(birthDate) >= requirement.MinAge)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Routing
Attribute Routing
[Route("api/[controller]")] // api/products
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet] // GET api/products
[HttpGet("{id:int}")] // GET api/products/5
[HttpGet("search/{name:alpha}")] // GET api/products/search/phone
[HttpPost] // POST api/products
[HttpPut("{id}")] // PUT api/products/5
[HttpDelete("{id}")] // DELETE api/products/5
}
Route Constraints
| Constraint | Example | Matches |
|---|---|---|
int |
{id:int} |
123 |
guid |
{id:guid} |
CD2C1638-… |
alpha |
{name:alpha} |
abc |
min(n) |
{age:min(18)} |
18+ |
regex(expr) |
{ssn:regex(^\d{{3}}-\d{{2}}-\d{{4}}$)} |
123-45-6789 |
Minimal APIs vs Controllers
Minimal APIs
app.MapGet("/items", async (IItemService service) =>
Results.Ok(await service.GetAllAsync()));
app.MapPost("/items", async (Item item, IItemService service) =>
{
await service.CreateAsync(item);
return Results.Created($"/items/{item.Id}", item);
});
Quick Comparison
| Feature | Minimal APIs | Controllers |
|---|---|---|
| Performance | Slightly faster | Standard |
| Model binding | Automatic | Automatic |
| Validation | Manual/Filter | Built-in |
| Filters | Yes (.NET 7+) | Full support |
| Best for | Simple APIs | Complex APIs |
Caching
Response Caching vs Output Caching
// Response Caching (HTTP headers, client/proxy caching)
[ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "id" })]
public IActionResult Get() { }
// Output Caching (.NET 7+, server-side)
app.MapGet("/data", [OutputCache(Duration = 60)] async () => GetData());
Cache Invalidation
// Tag-based invalidation
app.MapGet("/products", [OutputCache(Tags = new[] { "products" })] () => GetProducts());
app.MapPost("/products", async (IOutputCacheStore cache) =>
{
await cache.EvictByTagAsync("products", default);
return Results.Ok();
});
Background Services
Basic BackgroundService
public class WorkerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await DoWorkAsync(stoppingToken);
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
// Graceful shutdown logic
await base.StopAsync(cancellationToken);
}
}
Common Interview Answers
Q: Middleware vs Filters?
- Middleware: Request pipeline level, all requests
- Filters: MVC/API level, specific to controllers/actions
Q: IOptions vs IOptionsSnapshot vs IOptionsMonitor?
IOptions<T>: Singleton, never changesIOptionsSnapshot<T>: Scoped, updates per requestIOptionsMonitor<T>: Singleton, supports OnChange
Q: How to handle scoped service in singleton?
- Use
IServiceScopeFactoryto create scope, resolve within using block
Q: JWT refresh token strategy?
- Short-lived access token + long-lived refresh token
- Refresh token rotation (invalidate old on use)
- Store refresh tokens in database for revocation
Q: Output caching vs Response caching?
- Output: Server-side, more control, .NET 7+
- Response: HTTP headers, client/proxy caching
Quick Debugging Tips
// See generated SQL (EF Core)
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connString)
.EnableSensitiveDataLogging()
.LogTo(Console.WriteLine, LogLevel.Information));
// Request logging
app.UseSerilogRequestLogging();
// Check DI registrations
foreach (var service in services)
Console.WriteLine($"{service.ServiceType.Name} -> {service.ImplementationType?.Name}");