[{"data":1,"prerenderedAt":334},["ShallowReactive",2],{"topic-dotnet-aspnet-core":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fdotnet.yml",".NET Core interview questions on the CLR, C# language features, ASP.NET Core, dependency injection, Entity Framework, and deployment — the backbone of modern Microsoft-stack backend development.","yml","dotnet",{},".NET Core",7,"frameworks\u002Fdotnet",1,"_nP5kVwZOMUX9nd4gS5Ozj5Q6zQOEmGtLONE3ue01Gw",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fdotnet-aspnet-core.yml","How ASP.NET Core is structured — the middleware pipeline, routing system, controller lifecycle, and configuration model that underpin every web project.",{},"ASP.NET Core",3,"aspnet-core","topics\u002Fdotnet-aspnet-core","Se1SFKOyWU_JOhRJL33xKxS2I5dPChpRxW6cPVxXEiM",[25,109,184,259],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"questionsCount":102,"related":103,"seo":104,"seoDescription":105,"stem":106,"subtopic":27,"topic":19,"topicSlug":21,"updated":107,"__hash__":108},"qa\u002Fdotnet\u002Faspnet-core\u002Fmiddleware.md","Middleware",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fdotnet\u002Faspnet-core\u002Fmiddleware",[41,46,50,54,58,62,66,70,74,78,82,86,90,94,98],{"id":42,"difficulty":43,"q":44,"a":45},"middleware-what-is","easy","What is middleware in ASP.NET Core and how does the request pipeline work?","**Middleware** are components assembled into a pipeline that processes every HTTP\nrequest and response. Each component can execute logic before *and* after calling\nthe next component, inspect or mutate the request\u002Fresponse, or short-circuit the\npipeline by not calling the next component.\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\n\u002F\u002F Each Use() adds a middleware component to the pipeline:\napp.Use(async (context, next) =>\n{\n    \u002F\u002F Before: runs on the way IN (request)\n    Console.WriteLine($\"→ {context.Request.Path}\");\n\n    await next(context);   \u002F\u002F call the next middleware\n\n    \u002F\u002F After: runs on the way OUT (response)\n    Console.WriteLine($\"← {context.Response.StatusCode}\");\n});\n\napp.MapGet(\"\u002F\", () => \"Hello World\");\n\napp.Run(); \u002F\u002F starts the pipeline\n```\n\nThe pipeline is a **chain of delegates** — each middleware receives an\n`HttpContext` and a `RequestDelegate` (`next`). The ASP.NET Core host\ncalls the first middleware; it calls `next`, which calls the second, and so on.\nThe response flows back through each component in reverse order.\n\n**Rule of thumb:** Think of the middleware pipeline as an onion — request goes\ninward through each layer, response comes back outward through the same layers\nin reverse.\n",{"id":47,"difficulty":43,"q":48,"a":49},"use-run-map","What is the difference between `Use`, `Run`, and `Map` in the middleware pipeline?","- **`Use`** — adds a middleware that calls `next` to continue the pipeline.\n- **`Run`** — adds a **terminal** middleware that never calls `next`; it ends the pipeline.\n- **`Map`** — **branches** the pipeline for requests matching a path prefix; the branch\n  runs independently and does not rejoin the main pipeline.\n\n```csharp\napp.Use(async (context, next) =>\n{\n    \u002F\u002F Runs for ALL requests; calls next to continue\n    context.Items[\"start\"] = DateTime.UtcNow;\n    await next(context);\n});\n\napp.Map(\"\u002Fhealth\", healthApp =>\n{\n    \u002F\u002F Branch: only requests to \u002Fhealth enter here\n    healthApp.Run(async context =>\n    {\n        \u002F\u002F Terminal: returns immediately; never calls next\n        await context.Response.WriteAsync(\"OK\");\n    });\n});\n\n\u002F\u002F Main pipeline continues for non-\u002Fhealth requests:\napp.Run(async context =>\n{\n    \u002F\u002F Terminal middleware for everything else\n    await context.Response.WriteAsync(\"Hello\");\n});\n```\n\n`MapWhen` is a more flexible variant that branches on any predicate:\n\n```csharp\napp.MapWhen(\n    ctx => ctx.Request.Headers.ContainsKey(\"X-Special\"),\n    specialApp => specialApp.Run(async ctx =>\n        await ctx.Response.WriteAsync(\"special path\")));\n```\n\n**Rule of thumb:** Use `Use` for cross-cutting concerns that must wrap all\nrequests. Use `Run` only at the end of a pipeline branch. Use `Map` to isolate\nsub-pipelines for specific path prefixes.\n",{"id":51,"difficulty":35,"q":52,"a":53},"middleware-ordering","Why does middleware ordering matter in ASP.NET Core and what is the recommended order?","Middleware runs **in registration order** on the request path and in **reverse order**\non the response path. Ordering determines which component sees the request first and\nwhich wraps which. Getting it wrong causes security holes or broken behavior.\n\n```csharp\n\u002F\u002F Microsoft's recommended ordering for a typical web API:\napp.UseExceptionHandler(\"\u002Ferror\");   \u002F\u002F 1. Catch all unhandled exceptions\napp.UseHsts();                       \u002F\u002F 2. Add Strict-Transport-Security header\napp.UseHttpsRedirection();           \u002F\u002F 3. Redirect HTTP → HTTPS\napp.UseStaticFiles();                \u002F\u002F 4. Serve static files (no auth needed)\napp.UseRouting();                    \u002F\u002F 5. Match routes (populates endpoint metadata)\napp.UseCors();                       \u002F\u002F 6. Apply CORS policy (must be after routing)\napp.UseAuthentication();             \u002F\u002F 7. Identify the user\napp.UseAuthorization();              \u002F\u002F 8. Enforce access rules\napp.UseOutputCache();                \u002F\u002F 9. Cache responses (must be after auth)\napp.MapControllers();                \u002F\u002F 10. Dispatch to controller actions\n```\n\nWrong ordering examples:\n- `UseAuthentication` before `UseRouting` means auth runs even for unmatched routes.\n- `UseCors` before `UseRouting` means CORS cannot read endpoint metadata to apply\n  per-endpoint policies.\n- `UseStaticFiles` after `UseAuthentication` applies auth to static asset requests\n  (usually unnecessary overhead).\n\n**Rule of thumb:** Exception handling goes first. Static files go before auth.\nRouting before CORS. Auth before authorization. Dispatch (MapControllers) goes last.\n",{"id":55,"difficulty":35,"q":56,"a":57},"custom-middleware-class","How do you write a custom middleware class in ASP.NET Core?","Two styles: **convention-based** (POCO class) and **interface-based** (`IMiddleware`).\nConvention-based is more common and requires no registration in DI as a singleton.\n\n```csharp\n\u002F\u002F Convention-based: constructor takes RequestDelegate; InvokeAsync takes HttpContext\npublic class RequestTimingMiddleware\n{\n    private readonly RequestDelegate _next;\n    private readonly ILogger\u003CRequestTimingMiddleware> _logger;\n\n    public RequestTimingMiddleware(RequestDelegate next,\n        ILogger\u003CRequestTimingMiddleware> logger)\n    {\n        _next = next;\n        _logger = logger;\n    }\n\n    public async Task InvokeAsync(HttpContext context)\n    {\n        var sw = Stopwatch.StartNew();\n        await _next(context);          \u002F\u002F call the rest of the pipeline\n        sw.Stop();\n        _logger.LogInformation(\"{Method} {Path} completed in {Ms} ms\",\n            context.Request.Method,\n            context.Request.Path,\n            sw.ElapsedMilliseconds);\n    }\n}\n\n\u002F\u002F Extension method for clean registration:\npublic static class RequestTimingMiddlewareExtensions\n{\n    public static IApplicationBuilder UseRequestTiming(\n        this IApplicationBuilder app) =>\n        app.UseMiddleware\u003CRequestTimingMiddleware>();\n}\n\n\u002F\u002F In Program.cs:\napp.UseRequestTiming();\n```\n\nThe **`IMiddleware`** style is created per-request from DI (supports scoped dependencies):\n\n```csharp\npublic class ScopedMiddleware : IMiddleware\n{\n    private readonly IScopedService _svc; \u002F\u002F scoped dep — works with IMiddleware\n\n    public ScopedMiddleware(IScopedService svc) => _svc = svc;\n\n    public async Task InvokeAsync(HttpContext context, RequestDelegate next)\n    {\n        _svc.DoSomething();\n        await next(context);\n    }\n}\n\n\u002F\u002F Must be registered in DI:\nbuilder.Services.AddTransient\u003CScopedMiddleware>();\napp.UseMiddleware\u003CScopedMiddleware>();\n```\n\n**Rule of thumb:** Use convention-based middleware for singleton \u002F transient\ndependencies. Use `IMiddleware` when you need scoped services injected directly\ninto the middleware constructor.\n",{"id":59,"difficulty":35,"q":60,"a":61},"short-circuit","What does \"short-circuiting\" the pipeline mean and when should you do it?","**Short-circuiting** means a middleware does **not** call `next` — it handles the\nrequest entirely and writes the response itself, preventing later middleware from\nrunning. This is how authentication middleware rejects unauthorized requests before\nthey reach the controller.\n\n```csharp\napp.Use(async (context, next) =>\n{\n    \u002F\u002F Check API key in header\n    if (!context.Request.Headers.TryGetValue(\"X-Api-Key\", out var key)\n        || key != \"secret-key\")\n    {\n        \u002F\u002F Short-circuit: respond 401 without calling next\n        context.Response.StatusCode = 401;\n        await context.Response.WriteAsync(\"Unauthorized\");\n        return; \u002F\u002F ← critical: do NOT call next\n    }\n\n    await next(context); \u002F\u002F valid key — continue to next middleware\n});\n\n\u002F\u002F Controller only runs if the API key was valid:\napp.MapControllers();\n```\n\nCommon scenarios for short-circuiting:\n- **Authentication \u002F authorization failures** — return 401 \u002F 403.\n- **Health checks** — return 200 OK immediately without running the full pipeline.\n- **Rate limiting** — return 429 Too Many Requests.\n- **CORS preflight** — handle OPTIONS requests without business logic.\n- **Request validation** — return 400 Bad Request for malformed input.\n\n**Rule of thumb:** Short-circuit as early in the pipeline as possible to avoid\nunnecessary work. Always set the correct HTTP status code before returning without\ncalling `next`.\n",{"id":63,"difficulty":35,"q":64,"a":65},"exception-handling-middleware","How does exception-handling middleware work in ASP.NET Core?","ASP.NET Core provides two built-in options: **`UseExceptionHandler`** for production\n(re-executes a different endpoint) and **`UseDeveloperExceptionPage`** for development\n(shows a detailed stack trace). You can also write your own.\n\n```csharp\nif (app.Environment.IsDevelopment())\n    app.UseDeveloperExceptionPage(); \u002F\u002F shows full stack trace — dev only!\nelse\n    app.UseExceptionHandler(\"\u002Ferror\"); \u002F\u002F re-executes \u002Ferror endpoint — production\n\n\u002F\u002F \u002Ferror endpoint reads the stored exception from HttpContext:\napp.Map(\"\u002Ferror\", (HttpContext ctx) =>\n{\n    var ex = ctx.Features.Get\u003CIExceptionHandlerFeature>()?.Error;\n    return Results.Problem(\n        title: \"An error occurred\",\n        detail: ex?.Message,\n        statusCode: 500);\n});\n```\n\nCustom exception middleware — catches exceptions from any later middleware:\n\n```csharp\npublic class GlobalExceptionMiddleware\n{\n    private readonly RequestDelegate _next;\n    private readonly ILogger _logger;\n\n    public GlobalExceptionMiddleware(RequestDelegate next,\n        ILogger\u003CGlobalExceptionMiddleware> logger)\n    {\n        _next = next; _logger = logger;\n    }\n\n    public async Task InvokeAsync(HttpContext context)\n    {\n        try\n        {\n            await _next(context); \u002F\u002F run the rest of the pipeline\n        }\n        catch (Exception ex)\n        {\n            _logger.LogError(ex, \"Unhandled exception for {Path}\",\n                context.Request.Path);\n\n            context.Response.StatusCode = 500;\n            context.Response.ContentType = \"application\u002Fjson\";\n            await context.Response.WriteAsJsonAsync(\n                new { error = \"An unexpected error occurred.\" });\n        }\n    }\n}\n```\n\n**Rule of thumb:** Always register exception-handling middleware **first** in the\npipeline so it wraps every subsequent component and can catch any unhandled exception.\n",{"id":67,"difficulty":35,"q":68,"a":69},"middleware-vs-filters","What is the difference between middleware and MVC action filters?","**Middleware** operates at the HTTP pipeline level — it sees raw `HttpContext` and\nruns for every request, including static files, health checks, and non-MVC routes.\n**Action filters** are part of the MVC framework and run only for controller actions;\nthey have access to `ActionContext`, model binding results, and action arguments.\n\n```csharp\n\u002F\u002F Middleware: applies to every request, no MVC context\napp.Use(async (ctx, next) =>\n{\n    \u002F\u002F ctx.Request, ctx.Response — raw HTTP only\n    \u002F\u002F No knowledge of controllers, actions, or models\n    await next(ctx);\n});\n\n\u002F\u002F Action filter: runs only for matched MVC endpoints\npublic class ValidateModelFilter : IActionFilter\n{\n    public void OnActionExecuting(ActionExecutingContext context)\n    {\n        \u002F\u002F Has access to model state, action arguments, controller\n        if (!context.ModelState.IsValid)\n            context.Result = new BadRequestObjectResult(context.ModelState);\n    }\n\n    public void OnActionExecuted(ActionExecutedContext context) { }\n}\n\n\u002F\u002F Register globally:\nbuilder.Services.AddControllers(options =>\n    options.Filters.Add\u003CValidateModelFilter>());\n```\n\n| Aspect | Middleware | Action Filter |\n|---|---|---|\n| Scope | All requests | MVC actions only |\n| Context | `HttpContext` | `ActionContext`, model state |\n| Ordering | Pipeline order | Filter pipeline (Auth→Resource→Action→Result) |\n| Best for | CORS, logging, auth | Validation, audit, action-level cross-cutting |\n\n**Rule of thumb:** Use middleware for concerns that apply to all HTTP traffic\n(logging, CORS, auth, rate limiting). Use filters when you need MVC context\n(model state, action arguments, controller metadata).\n",{"id":71,"difficulty":43,"q":72,"a":73},"built-in-middleware","What are the most important built-in middleware components in ASP.NET Core?","ASP.NET Core ships with a rich set of built-in middleware for common scenarios:\n\n```csharp\napp.UseExceptionHandler(\"\u002Ferror\");      \u002F\u002F Global exception handling\napp.UseHsts();                          \u002F\u002F HTTP Strict Transport Security\napp.UseHttpsRedirection();              \u002F\u002F HTTP → HTTPS redirect\napp.UseStaticFiles();                   \u002F\u002F Serve wwwroot static assets\napp.UseRouting();                       \u002F\u002F Endpoint routing\napp.UseCors(\"MyPolicy\");               \u002F\u002F Cross-Origin Resource Sharing\napp.UseAuthentication();               \u002F\u002F JWT \u002F cookie auth\napp.UseAuthorization();                \u002F\u002F Policy enforcement\napp.UseRateLimiter();                  \u002F\u002F Rate limiting (.NET 7+)\napp.UseOutputCache();                  \u002F\u002F Response caching (.NET 7+)\napp.UseResponseCompression();          \u002F\u002F Gzip \u002F Brotli compression\napp.UseSession();                      \u002F\u002F Session state\napp.UseRequestLocalization();          \u002F\u002F i18n \u002F locale\n```\n\nThe `UseStaticFiles` middleware serves files from `wwwroot` by default:\n\n```csharp\n\u002F\u002F Serve files from a custom directory:\napp.UseStaticFiles(new StaticFileOptions\n{\n    FileProvider = new PhysicalFileProvider(\n        Path.Combine(builder.Environment.ContentRootPath, \"uploads\")),\n    RequestPath = \"\u002Fuploads\"\n});\n```\n\n**Rule of thumb:** Rely on built-in middleware before writing custom components —\nthey are battle-tested, well-configured, and maintained by Microsoft. Only write\ncustom middleware for genuinely application-specific concerns.\n",{"id":75,"difficulty":35,"q":76,"a":77},"middleware-dependency-injection","How do you inject services into middleware, and what lifetime restrictions apply?","In **convention-based middleware** the middleware class is instantiated once (singleton\nlifetime). **Singleton** services can be injected via the constructor. **Scoped** and\n**transient** services must be injected via `InvokeAsync` parameters — ASP.NET Core\nresolves them from the per-request scope automatically.\n\n```csharp\npublic class AuditMiddleware\n{\n    private readonly RequestDelegate _next;\n    private readonly IConfiguration _config; \u002F\u002F singleton — safe in constructor\n\n    public AuditMiddleware(RequestDelegate next, IConfiguration config)\n    {\n        _next = next;\n        _config = config;\n    }\n\n    \u002F\u002F Scoped service injected via parameter — fresh instance per request:\n    public async Task InvokeAsync(HttpContext context, IAuditService auditService)\n    {\n        await auditService.LogRequestAsync(context.Request.Path);\n        await _next(context);\n    }\n}\n```\n\nInjecting a scoped service into the constructor of convention-based middleware\ncreates a **captive dependency** — the scoped service lives for the entire app\nlifetime, causing shared state bugs across requests.\n\n**Rule of thumb:** Singleton deps → constructor. Scoped \u002F transient deps →\n`InvokeAsync` parameters. Alternatively, use `IMiddleware` which is resolved\nper-request and supports any lifetime in the constructor.\n",{"id":79,"difficulty":35,"q":80,"a":81},"response-caching-middleware","How does `UseResponseCaching` work and when should you use `UseOutputCache` instead?","**`UseResponseCaching`** (all .NET versions) adds standard HTTP cache headers\n(`Cache-Control`) and caches responses in memory that have those headers. It only\ncaches GET\u002FHEAD requests with successful 2xx status codes and does not cache\nwhen the request includes `Authorization` headers by default.\n\n```csharp\n\u002F\u002F Startup:\nbuilder.Services.AddResponseCaching();\napp.UseResponseCaching();   \u002F\u002F must be before routing\n\n\u002F\u002F Controller action:\n[HttpGet(\"products\")]\n[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)]\npublic IActionResult GetProducts() => Ok(_products);\n\u002F\u002F Sets: Cache-Control: public, max-age=60\n```\n\n**`UseOutputCache`** (.NET 7+) is a newer, more powerful replacement:\n\n```csharp\nbuilder.Services.AddOutputCache(options =>\n    options.AddPolicy(\"products\", p => p.Expire(TimeSpan.FromMinutes(1))));\n\napp.UseOutputCache();\n\napp.MapGet(\"products\", () => products).CacheOutput(\"products\");\n\u002F\u002F Supports cache invalidation, vary-by-query, tags, and custom stores\n```\n\n| Feature | ResponseCaching | OutputCache |\n|---|---|---|\n| Standard | HTTP\u002F1.1 Cache-Control | Server-side only |\n| Invalidation | None | Tag-based |\n| Vary-by | Headers | Query, headers, claims, custom |\n| .NET version | All | .NET 7+ |\n\n**Rule of thumb:** For new projects on .NET 7+, prefer `UseOutputCache` — it gives\nfine-grained control, tag-based invalidation, and works correctly with authenticated\nendpoints. Use `UseResponseCaching` only for simple public CDN-friendly responses.\n",{"id":83,"difficulty":35,"q":84,"a":85},"cors-middleware","How do you configure CORS middleware in ASP.NET Core?","**CORS (Cross-Origin Resource Sharing)** is enforced by the browser for\ncross-origin requests. The ASP.NET Core CORS middleware adds the necessary\n`Access-Control-Allow-*` response headers.\n\n```csharp\n\u002F\u002F Register CORS with a named policy:\nbuilder.Services.AddCors(options =>\n{\n    options.AddPolicy(\"AllowFrontend\", policy =>\n        policy.WithOrigins(\"https:\u002F\u002Fapp.example.com\")\n              .WithMethods(\"GET\", \"POST\", \"PUT\", \"DELETE\")\n              .WithHeaders(\"Content-Type\", \"Authorization\")\n              .AllowCredentials());\n\n    \u002F\u002F Development: allow any origin (NEVER use in production):\n    options.AddPolicy(\"DevAllowAll\", policy =>\n        policy.AllowAnyOrigin()\n              .AllowAnyMethod()\n              .AllowAnyHeader());\n});\n\n\u002F\u002F Apply: MUST be between UseRouting and UseAuthorization\napp.UseRouting();\napp.UseCors(\"AllowFrontend\");\napp.UseAuthorization();\n\n\u002F\u002F Per-endpoint override:\napp.MapGet(\"\u002Fpublic\", () => \"public\")\n   .RequireCors(\"DevAllowAll\");\n\n\u002F\u002F Disable CORS for a specific endpoint:\napp.MapGet(\"\u002Finternal\", () => \"internal\")\n   .RequireCors(builder => builder.DisallowCredentials());\n```\n\nThe preflight `OPTIONS` request is handled automatically by `UseCors` — you do not\nneed a separate OPTIONS action in your controller.\n\n**Rule of thumb:** Define tight CORS policies per environment — never use\n`AllowAnyOrigin` with `AllowCredentials` (the spec forbids it and ASP.NET Core\nthrows). Register `UseCors` between `UseRouting` and `UseAuthorization`.\n",{"id":87,"difficulty":43,"q":88,"a":89},"middleware-pipeline-diagram","Can you describe the lifecycle of a request through the ASP.NET Core middleware pipeline?","A request travels through the pipeline in a strict sequence:\n\n```\nHTTP Request\n     │\n     ▼\n┌─────────────────────────┐\n│ ExceptionHandler        │ ← wraps all; catches any exception\n│  ┌───────────────────┐  │\n│  │ HSTS \u002F HTTPS      │  │ ← security headers\n│  │  ┌─────────────┐  │  │\n│  │  │ StaticFiles │  │  │ ← served without auth\n│  │  │  ┌────────┐ │  │  │\n│  │  │  │Routing │ │  │  │ ← matches endpoint, populates metadata\n│  │  │  │  ┌───┐ │ │  │  │\n│  │  │  │  │Auth│ │ │  │  │ ← authn + authz\n│  │  │  │  │ ┌─┐│ │ │  │  │\n│  │  │  │  │ │EP│ │ │  │  │ ← endpoint (controller action \u002F minimal API)\n│  │  │  │  │ └─┘│ │ │  │  │\n│  │  │  │  └───┘ │ │  │  │\n│  │  │  └────────┘ │  │  │\n│  │  └─────────────┘  │  │\n│  └───────────────────┘  │\n└─────────────────────────┘\n     │\n     ▼\nHTTP Response (flows back outward through each layer)\n```\n\nEach box represents a middleware. The response flows **back outward** through each\nlayer in reverse, allowing each component to inspect or modify the response.\n\n```csharp\n\u002F\u002F Simplified but accurate lifecycle in code:\napp.UseExceptionHandler(\"\u002Ferror\");   \u002F\u002F outer: catches exceptions\napp.UseHttpsRedirection();\napp.UseStaticFiles();\napp.UseRouting();\napp.UseAuthentication();\napp.UseAuthorization();\napp.MapControllers();               \u002F\u002F inner: business logic\n```\n\n**Rule of thumb:** The request flows inward (top → bottom in registration order),\nthe response flows outward (bottom → top). Components registered early have the\nmost control and the most responsibility.\n",{"id":91,"difficulty":35,"q":92,"a":93},"rate-limiting-middleware","How do you add rate limiting to an ASP.NET Core application with `UseRateLimiter`?","**Rate limiting** (.NET 7+) is built into ASP.NET Core via `Microsoft.AspNetCore.RateLimiting`.\nYou define one or more policies, register them with `AddRateLimiter`, and apply the\nmiddleware with `UseRateLimiter`.\n\n```csharp\nusing System.Threading.RateLimiting;\n\nbuilder.Services.AddRateLimiter(options =>\n{\n    \u002F\u002F Fixed window: max 100 requests per 60-second window per client IP\n    options.AddFixedWindowLimiter(\"fixed\", o =>\n    {\n        o.PermitLimit         = 100;\n        o.Window              = TimeSpan.FromSeconds(60);\n        o.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;\n        o.QueueLimit          = 10; \u002F\u002F queue up to 10 excess requests\n    });\n\n    \u002F\u002F Sliding window — smoother than fixed; avoids burst at window boundary\n    options.AddSlidingWindowLimiter(\"sliding\", o =>\n    {\n        o.PermitLimit         = 100;\n        o.Window              = TimeSpan.FromSeconds(60);\n        o.SegmentsPerWindow   = 6; \u002F\u002F 10-second segments\n        o.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;\n        o.QueueLimit          = 0;\n    });\n\n    \u002F\u002F Token bucket — allows bursts up to bucket size\n    options.AddTokenBucketLimiter(\"api\", o =>\n    {\n        o.TokenLimit          = 50;\n        o.TokensPerPeriod     = 10;\n        o.ReplenishmentPeriod = TimeSpan.FromSeconds(10);\n        o.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;\n        o.QueueLimit          = 0;\n    });\n\n    \u002F\u002F 429 response when limit exceeded:\n    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;\n});\n\n\u002F\u002F Register in pipeline AFTER UseRouting:\napp.UseRateLimiter();\n\n\u002F\u002F Apply a policy to a specific endpoint:\napp.MapGet(\"\u002Fapi\u002Fdata\", () => \"data\")\n   .RequireRateLimiting(\"fixed\");\n\n\u002F\u002F Apply globally to all controllers:\napp.MapControllers().RequireRateLimiting(\"api\");\n\n\u002F\u002F Disable on a specific action:\napp.MapGet(\"\u002Fhealth\", () => \"OK\")\n   .DisableRateLimiting();\n```\n\n**Rule of thumb:** Prefer `AddTokenBucketLimiter` for APIs that need burst tolerance,\nand `AddSlidingWindowLimiter` for smooth, predictable throttling. Always set\n`RejectionStatusCode = 429` and consider partition by user or IP for fairness.\n",{"id":95,"difficulty":35,"q":96,"a":97},"use-when-vs-map-when","What is the difference between `UseWhen` and `MapWhen` for conditional middleware?","Both `UseWhen` and `MapWhen` branch the middleware pipeline based on a predicate, but\nthey differ in whether the branch **rejoins** the main pipeline:\n\n- **`UseWhen`** — runs extra middleware for matching requests, then **rejoins** the\n  main pipeline after the branch, so subsequent middleware still runs.\n- **`MapWhen`** — creates a completely separate branch; matching requests **never\n  return** to the main pipeline.\n\n```csharp\n\u002F\u002F UseWhen: add logging only for API requests, then continue main pipeline\napp.UseWhen(\n    ctx => ctx.Request.Path.StartsWithSegments(\"\u002Fapi\"),\n    apiApp =>\n    {\n        apiApp.Use(async (context, next) =>\n        {\n            Console.WriteLine($\"API request: {context.Request.Method} {context.Request.Path}\");\n            await next(context); \u002F\u002F continue branch\n        });\n        \u002F\u002F After the branch, the main pipeline continues — MapControllers still runs\n    });\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.MapControllers(); \u002F\u002F reached by ALL requests (both API and non-API)\n\n\u002F\u002F MapWhen: isolate \u002Fhealth — it never hits auth or controllers\napp.MapWhen(\n    ctx => ctx.Request.Path == \"\u002Fhealth\",\n    healthApp =>\n    {\n        \u002F\u002F Terminal for \u002Fhealth requests — does NOT rejoin main pipeline\n        healthApp.Run(async ctx =>\n            await ctx.Response.WriteAsync(\"Healthy\"));\n    });\n\n\u002F\u002F Good: UseWhen for cross-cutting concerns that still need the main pipeline\n\u002F\u002F Good: MapWhen for isolated sub-pipelines (health, metrics, webhooks)\n```\n\n**Rule of thumb:** Use `UseWhen` when you want conditional middleware but still need\nthe rest of the pipeline (auth, routing, dispatch) to run. Use `MapWhen` when the\nbranch is fully self-contained and must not fall through to the main pipeline.\n",{"id":99,"difficulty":35,"q":100,"a":101},"endpoint-filters","What are endpoint filters in minimal APIs and how do they compare to action filters?","**Endpoint filters** (ASP.NET Core 7+) are the minimal API equivalent of MVC action\nfilters. They wrap a minimal API handler and can run logic before and after it\nexecutes, short-circuit with a different result, or modify the response.\n\n```csharp\n\u002F\u002F Inline endpoint filter:\napp.MapPost(\"\u002Forders\", CreateOrder)\n   .AddEndpointFilter(async (ctx, next) =>\n   {\n       \u002F\u002F Before handler — validate request:\n       if (!ctx.HttpContext.User.Identity!.IsAuthenticated)\n           return Results.Unauthorized();\n\n       var result = await next(ctx); \u002F\u002F call the handler\n       \u002F\u002F After handler — inspect\u002Fmodify result:\n       return result;\n   });\n\n\u002F\u002F Reusable filter class:\npublic class ValidationFilter\u003CT> : IEndpointFilter\n{\n    public async ValueTask\u003Cobject?> InvokeAsync(\n        EndpointFilterInvocationContext ctx, EndpointFilterDelegate next)\n    {\n        \u002F\u002F Find the first argument of type T:\n        var arg = ctx.Arguments.OfType\u003CT>().FirstOrDefault();\n        if (arg is null)\n            return Results.BadRequest(\"Missing request body\");\n\n        \u002F\u002F Validate with DataAnnotations:\n        var errors = new List\u003CValidationResult>();\n        if (!Validator.TryValidateObject(arg, new ValidationContext(arg), errors, true))\n            return Results.ValidationProblem(\n                errors.ToDictionary(e => e.MemberNames.First(), e => new[] { e.ErrorMessage! }));\n\n        return await next(ctx); \u002F\u002F all valid — run handler\n    }\n}\n\n\u002F\u002F Apply typed validation filter to an endpoint:\napp.MapPost(\"\u002Fproducts\", (CreateProductDto dto) => CreateProduct(dto))\n   .AddEndpointFilter\u003CValidationFilter\u003CCreateProductDto>>();\n\n\u002F\u002F Apply to an entire group:\nvar orders = app.MapGroup(\"\u002Forders\").AddEndpointFilter\u003CValidationFilter\u003CCreateOrderDto>>();\norders.MapPost(\"\u002F\", CreateOrder);\norders.MapPut(\"\u002F{id:int}\", UpdateOrder);\n```\n\n| Aspect | Endpoint Filter | Action Filter |\n|---|---|---|\n| Target | Minimal API endpoints | MVC controller actions |\n| Context | `EndpointFilterInvocationContext` | `ActionExecutingContext` |\n| Groups | Yes (`MapGroup`) | Global \u002F controller \u002F action |\n| Model state | Manual validation | Automatic with `[ApiController]` |\n\n**Rule of thumb:** Use endpoint filters in minimal APIs for cross-cutting concerns\n(validation, logging, rate limiting). Use action filters in controller-based APIs\nwhere you need access to `ModelState`, action descriptors, or MVC conventions.\n",15,null,{"description":32},"ASP.NET Core middleware interview questions — request pipeline, Use vs Run vs Map, custom middleware, ordering, and short-circuiting.","dotnet\u002Faspnet-core\u002Fmiddleware","2026-06-23","8vRg6odXkVV3Jro2R3wievDNQDEkeqxD0pQKKa6Jf0A",{"id":110,"title":111,"body":112,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":116,"navigation":38,"order":33,"path":117,"questions":118,"questionsCount":102,"related":103,"seo":180,"seoDescription":181,"stem":182,"subtopic":111,"topic":19,"topicSlug":21,"updated":107,"__hash__":183},"qa\u002Fdotnet\u002Faspnet-core\u002Frouting.md","Routing",{"type":29,"value":113,"toc":114},[],{"title":32,"searchDepth":33,"depth":33,"links":115},[],{},"\u002Fdotnet\u002Faspnet-core\u002Frouting",[119,123,127,131,135,139,143,147,151,155,159,164,168,172,176],{"id":120,"difficulty":43,"q":121,"a":122},"routing-what-is","What is routing in ASP.NET Core and how does endpoint routing work?","**Routing** maps incoming HTTP requests to endpoints (controller actions, Razor Pages,\nor minimal API handlers) based on the URL path, HTTP method, and other metadata.\n**Endpoint routing** (introduced in ASP.NET Core 3.0) separates route *matching*\n(`UseRouting`) from route *execution* (`MapControllers`), allowing middleware in\nbetween to read endpoint metadata before the action runs.\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\n\napp.UseRouting();          \u002F\u002F 1. Match the request to an endpoint\napp.UseAuthentication();   \u002F\u002F 2. Run auth AFTER matching (can see endpoint metadata)\napp.UseAuthorization();    \u002F\u002F 3. Enforce authorization policy from endpoint metadata\napp.MapControllers();      \u002F\u002F 4. Execute the matched controller action\n\napp.Run();\n```\n\nThe key advantage of this design: `UseAuthorization` can read the `[Authorize]`\nattribute on the matched endpoint *before* dispatching, so it can reject unauthorized\nrequests without ever entering the controller method.\n\n**Rule of thumb:** Always place `UseRouting` before `UseAuthentication` \u002F\n`UseAuthorization`, and those before `MapControllers`. This is the order that lets\nmiddleware inspect endpoint metadata correctly.\n",{"id":124,"difficulty":43,"q":125,"a":126},"conventional-vs-attribute-routing","What is the difference between conventional routing and attribute routing?","**Conventional routing** defines URL patterns in a central place (`MapControllerRoute`).\n**Attribute routing** places route templates directly on controllers and actions with\n`[Route]`, `[HttpGet]`, `[HttpPost]`, etc.\n\n```csharp\n\u002F\u002F Conventional routing — one pattern matches many controllers:\napp.MapControllerRoute(\n    name: \"default\",\n    pattern: \"{controller=Home}\u002F{action=Index}\u002F{id?}\");\n\u002F\u002F \u002FProducts\u002FDetails\u002F5  → ProductsController.Details(id: 5)\n\u002F\u002F \u002FHome               → HomeController.Index()\n\n\u002F\u002F Attribute routing — template lives on the action:\n[ApiController]\n[Route(\"api\u002F[controller]\")]          \u002F\u002F api\u002Fproducts\npublic class ProductsController : ControllerBase\n{\n    [HttpGet]                        \u002F\u002F GET api\u002Fproducts\n    public IActionResult List() => Ok(_products);\n\n    [HttpGet(\"{id:int}\")]            \u002F\u002F GET api\u002Fproducts\u002F5\n    public IActionResult Get(int id) => Ok(Find(id));\n\n    [HttpPost]                       \u002F\u002F POST api\u002Fproducts\n    public IActionResult Create([FromBody] Product p) { ... }\n\n    [HttpDelete(\"{id:int}\")]         \u002F\u002F DELETE api\u002Fproducts\u002F5\n    public IActionResult Delete(int id) { ... }\n}\n```\n\n| Aspect | Conventional | Attribute |\n|---|---|---|\n| Route definition | `Program.cs` | Decorators on class\u002Fmethod |\n| Best for | MVC views (consistent URL structure) | REST APIs (per-action control) |\n| Override | Attribute wins | N\u002FA — all attribute |\n| Required for `[ApiController]` | No | Yes |\n\n**Rule of thumb:** Use conventional routing for MVC Razor view apps where URLs follow\na predictable pattern. Use attribute routing for Web APIs — `[ApiController]` requires it.\n",{"id":128,"difficulty":35,"q":129,"a":130},"route-constraints","What are route constraints and how do you use them?","**Route constraints** restrict which requests match a route parameter by type,\nformat, or range. They are specified in the route template with a colon (`:`).\n\n```csharp\n\u002F\u002F Built-in constraints:\n[HttpGet(\"{id:int}\")]              \u002F\u002F id must parse as int\n[HttpGet(\"{id:int:min(1)}\")]       \u002F\u002F int ≥ 1\n[HttpGet(\"{slug:alpha}\")]          \u002F\u002F letters only\n[HttpGet(\"{id:guid}\")]             \u002F\u002F GUID format\n[HttpGet(\"{date:datetime}\")]       \u002F\u002F parseable DateTime\n[HttpGet(\"{code:length(5)}\")]      \u002F\u002F exactly 5 characters\n[HttpGet(\"{code:regex(^[A-Z]{{3}}\\\\d{{2}}$)}\")]  \u002F\u002F regex pattern\n\n\u002F\u002F Example — accept only positive integer product ids:\n[HttpGet(\"products\u002F{id:int:min(1)}\")]\npublic IActionResult GetProduct(int id)\n{\n    var product = _repo.Find(id);\n    return product is null ? NotFound() : Ok(product);\n}\n\n\u002F\u002F Minimal API style:\napp.MapGet(\"\u002Forders\u002F{id:int}\", (int id) => FindOrder(id));\n```\n\nCustom constraint:\n\n```csharp\npublic class EvenNumberConstraint : IRouteConstraint\n{\n    public bool Match(HttpContext? httpContext, IRouter? route,\n        string routeKey, RouteValueDictionary values, RouteDirection direction)\n    {\n        return values.TryGetValue(routeKey, out var val)\n            && int.TryParse(val?.ToString(), out int n)\n            && n % 2 == 0;\n    }\n}\n\n\u002F\u002F Register:\nbuilder.Services.Configure\u003CRouteOptions>(o =>\n    o.ConstraintMap.Add(\"even\", typeof(EvenNumberConstraint)));\n\n\u002F\u002F Use:\napp.MapGet(\"\u002Fitems\u002F{id:even}\", (int id) => $\"Even id: {id}\");\n```\n\n**Rule of thumb:** Prefer built-in constraints for type and range validation.\nRoute constraints are for routing — not business validation. Always validate\ninput again in your action method or command handler.\n",{"id":132,"difficulty":43,"q":133,"a":134},"route-templates","How do route templates work — what are literal segments, parameters, and catch-all parameters?","A route template is a pattern string composed of **literal segments**, **route\nparameters** (`{name}`), optional parameters (`{name?}`), default values\n(`{name=default}`), and **catch-all parameters** (`{**rest}` or `{*rest}`).\n\n```csharp\n\u002F\u002F Literal segment — must match exactly:\n[HttpGet(\"api\u002Fv1\u002Fproducts\")]        \u002F\u002F only matches \u002Fapi\u002Fv1\u002Fproducts\n\n\u002F\u002F Route parameter — captures a URL segment:\n[HttpGet(\"products\u002F{id}\")]          \u002F\u002F captures: id = \"42\" for \u002Fproducts\u002F42\n\n\u002F\u002F Optional parameter:\n[HttpGet(\"search\u002F{term?}\")]         \u002F\u002F matches \u002Fsearch and \u002Fsearch\u002Fshoes\npublic IActionResult Search(string? term) { ... }\n\n\u002F\u002F Default value:\n[HttpGet(\"page\u002F{number=1}\")]        \u002F\u002F \u002Fpage → number=1; \u002Fpage\u002F3 → number=3\n\n\u002F\u002F Catch-all — captures the remainder including slashes:\n[HttpGet(\"files\u002F{**path}\")]         \u002F\u002F \u002Ffiles\u002Fa\u002Fb\u002Fc → path = \"a\u002Fb\u002Fc\"\npublic IActionResult GetFile(string path) { ... }\n\n\u002F\u002F Multiple parameters:\n[HttpGet(\"{year:int}\u002F{month:int}\u002F{slug}\")]\n\u002F\u002F \u002F2026\u002F6\u002Fmy-post → year=2026, month=6, slug=\"my-post\"\npublic IActionResult GetPost(int year, int month, string slug) { ... }\n```\n\nToken replacement in attribute routing:\n\n```csharp\n[Route(\"api\u002F[controller]\")]   \u002F\u002F [controller] → \"Products\" (class name minus suffix)\n[Route(\"api\u002F[controller]\u002F[action]\")]  \u002F\u002F [action] → method name\n```\n\n**Rule of thumb:** Use `{name}` for required segments, `{name?}` for optional\nsegments, and `{**rest}` for path-like catch-all values that include `\u002F`.\n",{"id":136,"difficulty":43,"q":137,"a":138},"http-method-attributes","What are the HTTP verb attributes (`[HttpGet]`, `[HttpPost]`, etc.) and how do they map to REST?","The HTTP verb attributes constrain a route to a specific HTTP method and optionally\nadd a path template. They implement the REST convention of using different HTTP\nmethods to represent different operations on the same resource URL.\n\n```csharp\n[ApiController]\n[Route(\"api\u002Forders\")]\npublic class OrdersController : ControllerBase\n{\n    [HttpGet]                          \u002F\u002F GET \u002Fapi\u002Forders — list all\n    public IActionResult List() => Ok(_orders);\n\n    [HttpGet(\"{id:int}\")]              \u002F\u002F GET \u002Fapi\u002Forders\u002F5 — get one\n    public IActionResult Get(int id) => Ok(Find(id));\n\n    [HttpPost]                         \u002F\u002F POST \u002Fapi\u002Forders — create\n    public IActionResult Create([FromBody] CreateOrderDto dto) { ... }\n\n    [HttpPut(\"{id:int}\")]              \u002F\u002F PUT \u002Fapi\u002Forders\u002F5 — full replace\n    public IActionResult Replace(int id, [FromBody] OrderDto dto) { ... }\n\n    [HttpPatch(\"{id:int}\")]            \u002F\u002F PATCH \u002Fapi\u002Forders\u002F5 — partial update\n    public IActionResult Update(int id, [FromBody] JsonPatchDocument\u003COrder> patch) { ... }\n\n    [HttpDelete(\"{id:int}\")]           \u002F\u002F DELETE \u002Fapi\u002Forders\u002F5 — delete\n    public IActionResult Delete(int id) { ... }\n\n    [HttpHead(\"{id:int}\")]             \u002F\u002F HEAD \u002Fapi\u002Forders\u002F5 — check existence\n    public IActionResult Exists(int id) { ... }\n}\n```\n\n`[AcceptVerbs(\"GET\", \"POST\")]` is the multi-verb variant:\n\n```csharp\n[AcceptVerbs(\"GET\", \"POST\")]\npublic IActionResult Search(string? q) { ... }\n```\n\n**Rule of thumb:** Follow REST conventions — GET for reads (idempotent, cacheable),\nPOST for creates, PUT for full updates, PATCH for partial updates, DELETE for\nremoval. Never use GET for actions with side effects.\n",{"id":140,"difficulty":35,"q":141,"a":142},"link-generation","How do you generate URLs in ASP.NET Core using `IUrlHelper` and `LinkGenerator`?","**`IUrlHelper`** is available in controllers and Razor Pages. **`LinkGenerator`**\nis a DI service usable anywhere, including middleware and background services.\n\n```csharp\n\u002F\u002F In a controller — IUrlHelper via Url property:\npublic class OrdersController : ControllerBase\n{\n    [HttpPost]\n    public IActionResult Create([FromBody] CreateOrderDto dto)\n    {\n        var order = _service.Create(dto);\n\n        \u002F\u002F Generate URL for the Get action:\n        var url = Url.Action(\n            action: \"Get\",\n            controller: \"Orders\",\n            values: new { id = order.Id });\n        \u002F\u002F → \u002Fapi\u002Forders\u002F42\n\n        return Created(url, order); \u002F\u002F 201 with Location header\n    }\n}\n\n\u002F\u002F Minimal APIs — LinkGenerator in DI:\napp.MapPost(\"\u002Forders\", (CreateOrderDto dto, LinkGenerator links) =>\n{\n    var order = CreateOrder(dto);\n    var url = links.GetPathByName(\"get-order\", new { id = order.Id });\n    return Results.Created(url, order);\n});\n\napp.MapGet(\"\u002Forders\u002F{id:int}\", (int id) => GetOrder(id))\n   .WithName(\"get-order\"); \u002F\u002F named endpoint for link generation\n```\n\n`Url.RouteUrl` uses the route name; `Url.Action` uses controller\u002Faction names.\n\n```csharp\n\u002F\u002F Using named routes:\n[HttpGet(\"{id:int}\", Name = \"GetOrder\")]\npublic IActionResult Get(int id) => Ok(Find(id));\n\nvar url = Url.RouteUrl(\"GetOrder\", new { id = 5 }); \u002F\u002F → \u002Fapi\u002Forders\u002F5\n```\n\n**Rule of thumb:** Always use `IUrlHelper` or `LinkGenerator` to generate URLs\nrather than hard-coding strings — route templates can change and named routes\nensure your links stay correct.\n",{"id":144,"difficulty":35,"q":145,"a":146},"areas","What are Areas in ASP.NET Core MVC and when do you use them?","**Areas** partition a large MVC application into functional groups, each with its\nown controllers, views, and models. They are useful for multi-module applications\n(e.g., Admin and Public sections with separate `HomeController` classes).\n\n```csharp\n\u002F\u002F Directory structure:\n\u002F\u002F Areas\u002F\n\u002F\u002F Admin\u002F\n\u002F\u002F Controllers\u002FProductsController.cs\n\u002F\u002F Views\u002FProducts\u002FIndex.cshtml\n\u002F\u002F Shop\u002F\n\u002F\u002F Controllers\u002FProductsController.cs\n\u002F\u002F Views\u002FProducts\u002FIndex.cshtml\n\n\u002F\u002F Controller — mark with [Area]:\n[Area(\"Admin\")]\n[Route(\"admin\u002F[controller]\u002F[action]\")]\npublic class ProductsController : Controller\n{\n    public IActionResult Index() => View();\n}\n\n\u002F\u002F Register area routing (in addition to conventional routing):\napp.MapControllerRoute(\n    name: \"areas\",\n    pattern: \"{area:exists}\u002F{controller=Home}\u002F{action=Index}\u002F{id?}\");\n\u002F\u002F \u002Fadmin\u002Fproducts → Admin area, ProductsController.Index\n\u002F\u002F \u002Fshop\u002Fproducts  → Shop area, ProductsController.Index\n```\n\nLink generation across areas requires specifying `area`:\n\n```csharp\n\u002F\u002F In a Razor view inside the Shop area, link to Admin:\n\u003Ca asp-area=\"Admin\" asp-controller=\"Products\" asp-action=\"Index\">\n    Admin Products\n\u003C\u002Fa>\n```\n\n**Rule of thumb:** Use Areas when a single project needs distinct sections with\noverlapping controller or view names (Admin, API, Shop). For true separation, prefer\nseparate projects. Areas add complexity — avoid them for small apps.\n",{"id":148,"difficulty":35,"q":149,"a":150},"minimal-apis-vs-controllers","What are minimal APIs and how do they compare to controller-based routing?","**Minimal APIs** (ASP.NET Core 6+) let you define HTTP endpoints with lambda\nexpressions directly in `Program.cs`, without controllers, attributes, or\n`ControllerBase`. They are simpler and have less overhead.\n\n```csharp\n\u002F\u002F Minimal API — no controller class needed:\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddDbContext\u003CAppDb>(o => o.UseInMemoryDatabase(\"orders\"));\n\nvar app = builder.Build();\n\napp.MapGet(\"\u002Forders\", async (AppDb db) =>\n    await db.Orders.ToListAsync());\n\napp.MapGet(\"\u002Forders\u002F{id:int}\", async (int id, AppDb db) =>\n    await db.Orders.FindAsync(id) is Order o ? Results.Ok(o) : Results.NotFound());\n\napp.MapPost(\"\u002Forders\", async (CreateOrderDto dto, AppDb db) =>\n{\n    var order = new Order { Item = dto.Item };\n    db.Orders.Add(order);\n    await db.SaveChangesAsync();\n    return Results.Created($\"\u002Forders\u002F{order.Id}\", order);\n});\n\napp.Run();\n```\n\n| Aspect | Minimal API | Controller |\n|---|---|---|\n| Boilerplate | Minimal | More (class, `ControllerBase`, attributes) |\n| Built-in features | Manual | Model binding, ModelState, filters, conventions |\n| Testability | Lambda — needs `WebApplicationFactory` | Easier to unit-test in isolation |\n| Organisation | Flat in Program.cs | Class-based, scales better |\n| Best for | Microservices, simple endpoints | Complex domains with many actions |\n\n**Rule of thumb:** Start with minimal APIs for new small services and microservices.\nSwitch to controllers when you need structured organization, action filters, complex\nmodel binding, or per-action conventions across many endpoints.\n",{"id":152,"difficulty":35,"q":153,"a":154},"route-groups","What are route groups in minimal APIs and how do they reduce boilerplate?","**Route groups** (`MapGroup`) let you apply a common prefix, middleware, filters,\nand metadata to a set of related endpoints without repeating them on each one.\n\n```csharp\nvar api = app.MapGroup(\"\u002Fapi\");          \u002F\u002F common prefix for all API routes\nvar v1  = api.MapGroup(\"\u002Fv1\");           \u002F\u002F \u002Fapi\u002Fv1\nvar orders = v1.MapGroup(\"\u002Forders\")\n               .RequireAuthorization()   \u002F\u002F all \u002Fapi\u002Fv1\u002Forders endpoints need auth\n               .WithTags(\"Orders\");      \u002F\u002F Swagger tag\n\norders.MapGet(\"\u002F\", GetAllOrders);        \u002F\u002F GET \u002Fapi\u002Fv1\u002Forders\norders.MapGet(\"\u002F{id:int}\", GetOrder);    \u002F\u002F GET \u002Fapi\u002Fv1\u002Forders\u002F5\norders.MapPost(\"\u002F\", CreateOrder);        \u002F\u002F POST \u002Fapi\u002Fv1\u002Forders\norders.MapDelete(\"\u002F{id:int}\", DeleteOrder); \u002F\u002F DELETE \u002Fapi\u002Fv1\u002Forders\u002F5\n\n\u002F\u002F Endpoint filters apply to all endpoints in the group:\norders.AddEndpointFilter(async (ctx, next) =>\n{\n    Console.WriteLine($\"Before: {ctx.HttpContext.Request.Path}\");\n    var result = await next(ctx);\n    Console.WriteLine(\"After\");\n    return result;\n});\n```\n\nWithout groups, each `MapGet`\u002F`MapPost` would repeat `\u002Fapi\u002Fv1\u002Forders` and\n`.RequireAuthorization()`.\n\n**Rule of thumb:** Use `MapGroup` to co-locate related endpoints and apply\nshared policies (auth, rate limiting, OpenAPI tags) in one place. Group by\nresource or feature — mirroring what controllers do naturally.\n",{"id":156,"difficulty":35,"q":157,"a":158},"parameter-binding-sources","Where can ASP.NET Core bind route and action parameters from, and how does it decide?","ASP.NET Core binds parameters from multiple sources. For controllers with\n`[ApiController]`, the binding source is inferred; without it, you must specify.\n\n```csharp\n[HttpGet(\"products\u002F{id}\")]\npublic IActionResult Get(\n    int id,                              \u002F\u002F [FromRoute] inferred — from URL segment\n    [FromQuery] string? sort,            \u002F\u002F from ?sort=name\n    [FromHeader(Name=\"X-Version\")] string version, \u002F\u002F from request header\n    [FromServices] IProductRepo repo)    \u002F\u002F injected from DI\n    => Ok(repo.Find(id, sort));\n\n[HttpPost(\"products\")]\npublic IActionResult Create(\n    [FromBody] CreateProductDto dto)    \u002F\u002F JSON body — inferred for complex type\n    => Created($\"\u002Fproducts\u002F{dto.Id}\", dto);\n\n\u002F\u002F Form data:\n[HttpPost(\"upload\")]\npublic IActionResult Upload(\n    [FromForm] string name,\n    IFormFile file) { ... }\n```\n\n**`[ApiController]` inference rules:**\n- Complex types → `[FromBody]`\n- `IFormFile` \u002F `IFormFileCollection` → `[FromForm]`\n- Route parameter matches parameter name → `[FromRoute]`\n- Everything else → `[FromQuery]`\n\nFor minimal APIs, parameters are bound the same way with additional support for\ndirect DI injection:\n\n```csharp\napp.MapGet(\"\u002Fproducts\u002F{id}\", (int id, IProductRepo repo) =>\n    repo.Find(id)); \u002F\u002F id from route, repo from DI\n```\n\n**Rule of thumb:** Use `[ApiController]` to get automatic binding inference. Add\nexplicit `[From*]` attributes when you need to override the default or when\nworking without `[ApiController]`.\n",{"id":160,"difficulty":161,"q":162,"a":163},"routing-ambiguity","hard","What happens when two routes are ambiguous and how does ASP.NET Core resolve conflicts?","When multiple routes match a request with equal specificity, ASP.NET Core throws\nan `AmbiguousMatchException` at runtime (not build time). You must resolve ambiguity\nby making routes more specific or by ordering them.\n\n```csharp\n\u002F\u002F Ambiguous: both match GET \u002Fapi\u002Fproducts\u002F5\n[HttpGet(\"{id}\")]\npublic IActionResult GetById(string id) { ... }\n\n[HttpGet(\"{name}\")]\npublic IActionResult GetByName(string name) { ... }\n\u002F\u002F ↑ AmbiguousMatchException — the runtime can't distinguish these\n\n\u002F\u002F Fix 1: use route constraints to differentiate:\n[HttpGet(\"{id:int}\")]              \u002F\u002F only matches integers → \u002Fproducts\u002F5\npublic IActionResult GetById(int id) { ... }\n\n[HttpGet(\"{name:alpha}\")]          \u002F\u002F only matches letters → \u002Fproducts\u002Fwidget\npublic IActionResult GetByName(string name) { ... }\n\n\u002F\u002F Fix 2: use different path templates:\n[HttpGet(\"by-id\u002F{id:int}\")]\npublic IActionResult GetById(int id) { ... }\n\n[HttpGet(\"by-name\u002F{name}\")]\npublic IActionResult GetByName(string name) { ... }\n```\n\nConventional routing processes routes in registration order — first match wins\n— so ordering matters there:\n\n```csharp\n\u002F\u002F More specific pattern FIRST:\napp.MapControllerRoute(\"specific\", \"products\u002Ffeatured\", ...);\napp.MapControllerRoute(\"default\",  \"products\u002F{id}\",     ...);\n```\n\n**Rule of thumb:** Design routes to be unambiguous using constraints or distinct\ntemplates. Rely on registration order only as a last resort — it is implicit and\neasy to break when adding new routes.\n",{"id":165,"difficulty":35,"q":166,"a":167},"fallback-routes","What is a fallback route and how do you configure one in ASP.NET Core?","A **fallback route** is matched only when no other endpoint matches the request.\nIt is commonly used in Single-Page Applications to serve `index.html` for every\nunmatched path so client-side routing (React Router, Vue Router) can take over.\n\n```csharp\n\u002F\u002F MapFallback — minimal API style:\napp.MapControllers();       \u002F\u002F controller endpoints matched first\n\n\u002F\u002F MapFallbackToFile: serves a static file for any unmatched path\napp.MapFallbackToFile(\"index.html\"); \u002F\u002F SPAs: catch all and serve the shell\n\n\u002F\u002F MapFallbackToController: forwards unmatched routes to a controller action\napp.MapFallbackToController(\"Index\", \"Home\");\n\u002F\u002F Any unmatched request → HomeController.Index\n\n\u002F\u002F MapFallback with a handler:\napp.MapFallback(async ctx =>\n{\n    ctx.Response.StatusCode = 404;\n    await ctx.Response.WriteAsJsonAsync(new\n    {\n        error = \"Route not found\",\n        path  = ctx.Request.Path.Value\n    });\n});\n\n\u002F\u002F Fallback for a specific prefix only:\napp.MapFallback(\"\u002Fapp\u002F{**slug}\", async ctx =>\n    await ctx.Response.WriteAsync(\"SPA shell\"));\n\u002F\u002F \u002Fapp\u002Fdashboard, \u002Fapp\u002Fsettings → all served by SPA shell\n\u002F\u002F Other paths → normal 404\n```\n\nFallbacks have **lower priority than any other endpoint**, including wildcard routes,\nso they never shadow real endpoints.\n\n**Rule of thumb:** Use `MapFallbackToFile(\"index.html\")` for SPA hosting. Use\n`MapFallback` with a custom handler to return a structured JSON 404 for APIs.\nNever use a catch-all route constraint (`{**}`) on a real controller action as a\nsubstitute — use the proper fallback API.\n",{"id":169,"difficulty":35,"q":170,"a":171},"endpoint-metadata","What is endpoint metadata and how do middleware components use it?","**Endpoint metadata** is information attached to a matched endpoint at routing time.\nIt includes attributes (`[Authorize]`, `[AllowAnonymous]`, `[RequireCors]`, OpenAPI\ntags, etc.) and custom data added with `WithMetadata`. Middleware reads metadata from\n`IEndpointFeature` to make decisions without inspecting the URL.\n\n```csharp\n\u002F\u002F Attach metadata to a minimal API endpoint:\napp.MapGet(\"\u002Forders\", GetOrders)\n   .RequireAuthorization(\"AdminOnly\")    \u002F\u002F adds AuthorizeAttribute metadata\n   .WithName(\"get-orders\")               \u002F\u002F adds IEndpointNameMetadata\n   .WithTags(\"Orders\")                   \u002F\u002F adds ITagsMetadata (OpenAPI)\n   .WithMetadata(new AuditAttribute());  \u002F\u002F custom metadata\n\n\u002F\u002F Middleware reading endpoint metadata after UseRouting:\napp.UseRouting(); \u002F\u002F route matching happens here — metadata is now available\n\napp.Use(async (ctx, next) =>\n{\n    var endpoint = ctx.GetEndpoint();\n    if (endpoint is not null)\n    {\n        \u002F\u002F Check for a custom audit attribute on the matched endpoint:\n        var audit = endpoint.Metadata.GetMetadata\u003CAuditAttribute>();\n        if (audit is not null)\n            await AuditLogAsync(ctx.Request.Path);\n    }\n    await next(ctx);\n});\n\napp.UseAuthorization(); \u002F\u002F reads AuthorizeAttribute metadata set by UseRouting\n\napp.MapControllers();\n```\n\nWithout `UseRouting` running first, `ctx.GetEndpoint()` returns `null` because no\nroute has been matched yet.\n\n**Rule of thumb:** Place any middleware that reads endpoint metadata **after**\n`UseRouting` and **before** `MapControllers`. This is the window where the endpoint\nis known but not yet executed.\n",{"id":173,"difficulty":35,"q":174,"a":175},"route-naming-and-order","How do you control the order and priority of routes in ASP.NET Core's endpoint routing?","Endpoint routing uses a **precedence algorithm** to rank routes, not simple\nregistration order. Literal segments rank higher than parameters, and constrained\nparameters rank higher than unconstrained ones. Registration order is used only\nas a tiebreaker when two routes are equally specific.\n\n```csharp\n\u002F\u002F Endpoint routing precedence examples (higher beats lower):\n\u002F\u002F 1. Literal segment:    \u002Fproducts\u002Ffeatured  (most specific)\n\u002F\u002F 2. Constrained param:  \u002Fproducts\u002F{id:int}\n\u002F\u002F 3. Unconstrained param:\u002Fproducts\u002F{id}\n\u002F\u002F 4. Catch-all:          \u002Fproducts\u002F{**rest}  (least specific)\n\n\u002F\u002F All registered in any order — routing picks correctly:\napp.MapGet(\"\u002Fproducts\u002Ffeatured\", GetFeatured);   \u002F\u002F literal — wins for \u002Fproducts\u002Ffeatured\napp.MapGet(\"\u002Fproducts\u002F{id:int}\", GetById);       \u002F\u002F constrained — wins for \u002Fproducts\u002F5\napp.MapGet(\"\u002Fproducts\u002F{slug}\",   GetBySlug);     \u002F\u002F unconstrained — wins for \u002Fproducts\u002Fwidget\napp.MapGet(\"\u002Fproducts\u002F{**rest}\", GetSubPath);    \u002F\u002F catch-all — last resort\n\n\u002F\u002F Explicit Order property to force rank when needed:\napp.MapGet(\"\u002Fdebug\", DebugHandler)\n   .WithOrder(-1); \u002F\u002F negative = higher priority; runs before default (0)\n\napp.MapControllerRoute(\n    name: \"default\",\n    pattern: \"{controller=Home}\u002F{action=Index}\u002F{id?}\");\n\u002F\u002F Conventional routes matched after attribute routes by default\n```\n\nFor conventional routes, registration order matters within the same specificity\ntier — register more specific patterns first:\n\n```csharp\n\u002F\u002F Specific conventional pattern must come before generic one:\napp.MapControllerRoute(\"blog\", \"blog\u002F{year:int}\u002F{slug}\", new { controller = \"Blog\", action = \"Post\" });\napp.MapControllerRoute(\"default\", \"{controller=Home}\u002F{action=Index}\u002F{id?}\");\n```\n\n**Rule of thumb:** Trust endpoint routing's precedence algorithm for attribute\nroutes — it handles most cases without explicit ordering. Add `.WithOrder()` only\nwhen you have a genuine tie that the algorithm cannot break. For conventional\nroutes, always register more specific patterns first.\n",{"id":177,"difficulty":35,"q":178,"a":179},"openapi-metadata","How do you add OpenAPI metadata to minimal API endpoints for Swagger documentation?","Minimal APIs use extension methods from `Microsoft.AspNetCore.OpenApi` (.NET 9) or\nSwashbuckle to attach **OpenAPI metadata** — names, descriptions, tags, response\ntypes, and operation IDs — directly to endpoint definitions.\n\n```csharp\n\u002F\u002F Install: dotnet add package Microsoft.AspNetCore.OpenApi  (.NET 9+)\nbuilder.Services.AddOpenApi(); \u002F\u002F registers OpenAPI document generation\n\napp.MapOpenApi(); \u002F\u002F exposes \u002Fopenapi\u002Fv1.json\n\n\u002F\u002F Attach metadata to individual endpoints:\napp.MapGet(\"\u002Fproducts\u002F{id:int}\", async (int id, IProductRepo repo) =>\n    await repo.FindAsync(id) is Product p ? Results.Ok(p) : Results.NotFound())\n   .WithName(\"GetProduct\")                \u002F\u002F operation id for link generation\n   .WithSummary(\"Get a product by ID\")    \u002F\u002F short description in Swagger UI\n   .WithDescription(\"Returns the product matching the given integer ID, or 404.\")\n   .WithTags(\"Products\")                  \u002F\u002F groups endpoints in Swagger UI\n   .Produces\u003CProduct>(StatusCodes.Status200OK)\n   .Produces(StatusCodes.Status404NotFound)\n   .RequireAuthorization();\n\napp.MapPost(\"\u002Fproducts\", async (CreateProductDto dto, IProductRepo repo) =>\n{\n    var product = await repo.CreateAsync(dto);\n    return Results.CreatedAtRoute(\"GetProduct\", new { id = product.Id }, product);\n})\n   .WithName(\"CreateProduct\")\n   .WithTags(\"Products\")\n   .Accepts\u003CCreateProductDto>(\"application\u002Fjson\")   \u002F\u002F documents request body type\n   .Produces\u003CProduct>(StatusCodes.Status201Created)\n   .Produces\u003CValidationProblemDetails>(StatusCodes.Status400BadRequest);\n\n\u002F\u002F Apply metadata to a whole group:\nvar v1 = app.MapGroup(\"\u002Fapi\u002Fv1\")\n            .WithTags(\"v1\")\n            .WithOpenApi(); \u002F\u002F enables OpenAPI for all endpoints in the group\n```\n\n**Rule of thumb:** Always attach `.WithName`, `.WithTags`, `.Produces\u003CT>`, and\n`.WithSummary` to every public endpoint. Accurate metadata drives Swagger UI,\nclient SDK generation, and integration test contracts — treat it as part of the\nendpoint definition, not an afterthought.\n",{"description":32},"ASP.NET Core routing interview questions — attribute vs conventional routing, route constraints, endpoint routing, link generation, and minimal APIs.","dotnet\u002Faspnet-core\u002Frouting","CoFNNHGBlyxWZipUlBETmBmbKu-a6eXzliQPovucrQo",{"id":185,"title":186,"body":187,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":191,"navigation":38,"order":20,"path":192,"questions":193,"questionsCount":102,"related":103,"seo":254,"seoDescription":255,"stem":256,"subtopic":257,"topic":19,"topicSlug":21,"updated":107,"__hash__":258},"qa\u002Fdotnet\u002Faspnet-core\u002Fcontrollers-actions.md","Controllers Actions",{"type":29,"value":188,"toc":189},[],{"title":32,"searchDepth":33,"depth":33,"links":190},[],{},"\u002Fdotnet\u002Faspnet-core\u002Fcontrollers-actions",[194,198,202,206,210,214,218,222,226,230,234,238,242,246,250],{"id":195,"difficulty":43,"q":196,"a":197},"controller-what-is","What is a controller in ASP.NET Core and what makes a class a controller?","A **controller** is a class that groups related HTTP endpoint handlers (actions).\nASP.NET Core identifies a controller by convention or attribute:\n- The class name ends in `Controller`, **or**\n- The class is decorated with `[Controller]`, **or**\n- The class inherits from `Controller` or `ControllerBase`.\n\n```csharp\n\u002F\u002F Inheriting ControllerBase — most common for APIs\n[ApiController]\n[Route(\"api\u002F[controller]\")]\npublic class ProductsController : ControllerBase\n{\n    private readonly IProductService _svc;\n\n    \u002F\u002F Dependencies injected via constructor (DI):\n    public ProductsController(IProductService svc) => _svc = svc;\n\n    [HttpGet]\n    public async Task\u003CIActionResult> List()\n    {\n        var products = await _svc.GetAllAsync();\n        return Ok(products); \u002F\u002F 200 OK with JSON body\n    }\n\n    [HttpGet(\"{id:int}\")]\n    public async Task\u003CIActionResult> Get(int id)\n    {\n        var p = await _svc.FindAsync(id);\n        return p is null ? NotFound() : Ok(p);\n    }\n}\n```\n\n`ControllerBase` provides helpers (`Ok`, `NotFound`, `BadRequest`, etc.) and\nmodel binding. `Controller` extends `ControllerBase` with Razor View support\n(`View()`, `ViewData`, `TempData`).\n\n**Rule of thumb:** Inherit from `ControllerBase` for Web APIs (no views). Inherit\nfrom `Controller` only for MVC apps that render Razor Views.\n",{"id":199,"difficulty":43,"q":200,"a":201},"iactionresult-vs-actionresult","What is the difference between `IActionResult`, `ActionResult\u003CT>`, and returning `T` directly?","- **`IActionResult`** — interface; allows returning any response (200, 404, 400, etc.)\n  but loses compile-time type information for the success body.\n- **`ActionResult\u003CT>`** — generic wrapper; allows returning both a typed result (`T`)\n  or any `IActionResult` (errors), and enables accurate OpenAPI schema generation.\n- **`T` directly** — simplest; always returns 200 OK with the serialized object.\n  No built-in way to return 404 or 400.\n\n```csharp\n\u002F\u002F IActionResult — flexible but no type info in OpenAPI:\n[HttpGet(\"{id}\")]\npublic IActionResult GetV1(int id)\n{\n    var p = _repo.Find(id);\n    return p is null ? NotFound() : Ok(p); \u002F\u002F Ok() wraps Product, but return type is IActionResult\n}\n\n\u002F\u002F ActionResult\u003CT> — preferred for APIs; swagger shows Product schema:\n[HttpGet(\"{id}\")]\npublic ActionResult\u003CProduct> GetV2(int id)\n{\n    var p = _repo.Find(id);\n    return p is null ? NotFound() : p; \u002F\u002F implicit conversion Product → ActionResult\u003CProduct>\n}\n\n\u002F\u002F T directly — minimal API style; no error responses possible:\n[HttpGet(\"all\")]\npublic IEnumerable\u003CProduct> List() => _repo.GetAll();\n\u002F\u002F Always 200 OK; can't return 404 without throwing an exception\n```\n\nWith `[ApiController]`, `ActionResult\u003CT>` also feeds `ProducesResponseType` inference.\n\n**Rule of thumb:** Use `ActionResult\u003CT>` for API actions that can return both a\ntyped success response and error responses. Use `T` directly only for actions that\nalways succeed and you are confident will never need to return an error status.\n",{"id":203,"difficulty":35,"q":204,"a":205},"apicontroller-attribute","What does the `[ApiController]` attribute do?","`[ApiController]` enables several **opinionated API behaviors** that reduce\nboilerplate and enforce best practices:\n\n1. **Automatic model validation** — if `ModelState` is invalid, automatically\n   returns `400 Bad Request` with a `ValidationProblemDetails` body.\n2. **Binding source inference** — infers `[FromBody]`, `[FromRoute]`, `[FromQuery]`\n   without explicit attributes.\n3. **Problem Details for error responses** — 400\u002F404 etc. are wrapped in RFC 7807\n   `ProblemDetails` JSON.\n4. **Attribute routing required** — conventional routing is disabled; every action\n   must have a route attribute.\n\n```csharp\n[ApiController]\n[Route(\"api\u002F[controller]\")]\npublic class OrdersController : ControllerBase\n{\n    [HttpPost]\n    public IActionResult Create([FromBody] CreateOrderDto dto)\n    {\n        \u002F\u002F WITHOUT [ApiController]: you'd need to check ModelState manually:\n        \u002F\u002F if (!ModelState.IsValid) return BadRequest(ModelState);\n\n        \u002F\u002F WITH [ApiController]: ModelState.IsValid check is automatic.\n        \u002F\u002F If dto.Name is missing, 400 is returned before this line runs:\n        var order = _svc.Create(dto);\n        return Created($\"\u002Fapi\u002Forders\u002F{order.Id}\", order);\n    }\n}\n\n\u002F\u002F DTO with validation attributes:\npublic class CreateOrderDto\n{\n    [Required]\n    [StringLength(100)]\n    public string ProductName { get; set; } = default!;\n\n    [Range(1, 1000)]\n    public int Quantity { get; set; }\n}\n```\n\nApply at the assembly level to avoid repeating on every controller:\n\n```csharp\n[assembly: ApiController]\n```\n\n**Rule of thumb:** Always add `[ApiController]` to Web API controllers. It reduces\nboilerplate and enforces conventions that make your API predictable. Do not apply\nit to MVC controllers that return Views.\n",{"id":207,"difficulty":35,"q":208,"a":209},"model-binding","How does model binding work in ASP.NET Core?","**Model binding** extracts values from the HTTP request (route data, query string,\nform data, body, headers) and converts them into action method parameter types.\nIt runs before the action method and populates `ModelState`.\n\n```csharp\n[HttpGet(\"search\")]\npublic IActionResult Search(\n    string term,           \u002F\u002F [FromQuery] inferred: ?term=shoes\n    int page = 1,          \u002F\u002F [FromQuery] with default\n    int pageSize = 20)     \u002F\u002F [FromQuery] with default\n    => Ok(_svc.Search(term, page, pageSize));\n\n[HttpPost]\npublic IActionResult Create([FromBody] CreateProductDto dto)\n    \u002F\u002F Body JSON is deserialized into CreateProductDto\n    => Created($\"\u002F{dto.Id}\", dto);\n\n[HttpPost(\"import\")]\npublic IActionResult Import(\n    [FromForm] string category,   \u002F\u002F form field\n    IFormFile file)               \u002F\u002F uploaded file\n{\n    using var stream = file.OpenReadStream();\n    _importer.Import(stream, category);\n    return Ok();\n}\n```\n\nCustom model binder:\n\n```csharp\n\u002F\u002F Bind a comma-separated query string to a List\u003Cint>:\npublic class CommaSeparatedListBinder : IModelBinder\n{\n    public Task BindModelAsync(ModelBindingContext ctx)\n    {\n        var value = ctx.ValueProvider.GetValue(ctx.ModelName).FirstValue;\n        if (string.IsNullOrEmpty(value)) { ctx.Result = ModelBindingResult.Success(null); return Task.CompletedTask; }\n        var result = value.Split(',').Select(int.Parse).ToList();\n        ctx.Result = ModelBindingResult.Success(result);\n        return Task.CompletedTask;\n    }\n}\n\n[HttpGet(\"filter\")]\npublic IActionResult Filter(\n    [ModelBinder(typeof(CommaSeparatedListBinder))] List\u003Cint> ids)\n    \u002F\u002F ?ids=1,2,3 → ids = [1, 2, 3]\n    => Ok(ids);\n```\n\n**Rule of thumb:** Rely on default binding for standard cases. Use explicit\n`[From*]` attributes when you need to override the inferred source or when\nnot using `[ApiController]`.\n",{"id":211,"difficulty":35,"q":212,"a":213},"model-validation","How do you validate models with Data Annotations and `ModelState`?",".NET's **Data Annotations** validate input before the action runs. `ModelState`\naccumulates the results; `[ApiController]` automatically returns 400 if invalid.\n\n```csharp\nusing System.ComponentModel.DataAnnotations;\n\npublic class RegisterDto\n{\n    [Required(ErrorMessage = \"Email is required\")]\n    [EmailAddress]\n    public string Email { get; set; } = default!;\n\n    [Required]\n    [StringLength(100, MinimumLength = 8,\n        ErrorMessage = \"Password must be 8–100 characters\")]\n    public string Password { get; set; } = default!;\n\n    [Range(18, 120, ErrorMessage = \"Age must be between 18 and 120\")]\n    public int Age { get; set; }\n\n    [Url]\n    public string? Website { get; set; }\n}\n\n[HttpPost(\"register\")]\npublic IActionResult Register([FromBody] RegisterDto dto)\n{\n    \u002F\u002F [ApiController] returns 400 automatically if ModelState is invalid.\n    \u002F\u002F Without [ApiController]:\n    if (!ModelState.IsValid)\n        return BadRequest(ModelState);\n\n    _svc.Register(dto);\n    return Ok();\n}\n```\n\nCustom validation attribute:\n\n```csharp\npublic class FutureDateAttribute : ValidationAttribute\n{\n    protected override ValidationResult? IsValid(\n        object? value, ValidationContext ctx)\n    {\n        if (value is DateTime dt && dt > DateTime.Today)\n            return ValidationResult.Success;\n        return new ValidationResult(\"Date must be in the future\");\n    }\n}\n\npublic class EventDto\n{\n    [FutureDate]\n    public DateTime StartDate { get; set; }\n}\n```\n\nFor complex cross-property validation, implement `IValidatableObject`:\n\n```csharp\npublic class DateRangeDto : IValidatableObject\n{\n    public DateTime Start { get; set; }\n    public DateTime End { get; set; }\n\n    public IEnumerable\u003CValidationResult> Validate(ValidationContext ctx)\n    {\n        if (End \u003C= Start)\n            yield return new ValidationResult(\"End must be after Start\",\n                new[] { nameof(End) });\n    }\n}\n```\n\n**Rule of thumb:** Use Data Annotations for simple, attribute-level validation.\nImplement `IValidatableObject` for cross-property rules. For complex domain\nvalidation (database lookups, business rules), validate in your service layer,\nnot on the DTO.\n",{"id":215,"difficulty":35,"q":216,"a":217},"action-filters","What are action filters and when should you use them?","**Action filters** are attributes that run code before (`OnActionExecuting`) and\nafter (`OnActionExecuted`) an action method. They have access to the full MVC\ncontext — action arguments, `ModelState`, and the `ActionResult`.\n\n```csharp\n\u002F\u002F Synchronous filter:\npublic class LoggingFilter : IActionFilter\n{\n    private readonly ILogger _logger;\n    public LoggingFilter(ILogger\u003CLoggingFilter> logger) => _logger = logger;\n\n    public void OnActionExecuting(ActionExecutingContext context)\n    {\n        _logger.LogInformation(\"Executing {Action} with args: {Args}\",\n            context.ActionDescriptor.DisplayName,\n            context.ActionArguments);\n    }\n\n    public void OnActionExecuted(ActionExecutedContext context)\n    {\n        _logger.LogInformation(\"Executed {Action} → {StatusCode}\",\n            context.ActionDescriptor.DisplayName,\n            (context.Result as ObjectResult)?.StatusCode);\n    }\n}\n\n\u002F\u002F Async filter (preferred when awaiting inside):\npublic class ValidateModelFilter : IAsyncActionFilter\n{\n    public async Task OnActionExecutionAsync(\n        ActionExecutingContext context, ActionExecutionDelegate next)\n    {\n        if (!context.ModelState.IsValid)\n        {\n            context.Result = new BadRequestObjectResult(context.ModelState);\n            return; \u002F\u002F short-circuit — do NOT call next\n        }\n        await next(); \u002F\u002F run the action\n    }\n}\n\n\u002F\u002F Apply globally:\nbuilder.Services.AddControllers(o =>\n{\n    o.Filters.Add\u003CLoggingFilter>();\n    o.Filters.Add\u003CValidateModelFilter>();\n});\n\n\u002F\u002F Apply to a controller or action:\n[ServiceFilter(typeof(LoggingFilter))] \u002F\u002F resolves from DI\npublic class OrdersController : ControllerBase { ... }\n```\n\nFilter execution order: Authorization → Resource → Action → Result → Exception.\n\n**Rule of thumb:** Use action filters for concerns that need MVC context (model\nstate, action arguments). Use middleware for concerns that apply to all HTTP traffic\nregardless of whether it reaches a controller.\n",{"id":219,"difficulty":35,"q":220,"a":221},"content-negotiation","What is content negotiation in ASP.NET Core and how is it configured?","**Content negotiation** is the process of selecting the response format (JSON, XML,\netc.) based on the client's `Accept` header. ASP.NET Core defaults to JSON via\n`System.Text.Json` but can be extended.\n\n```csharp\n\u002F\u002F Add XML formatter alongside JSON:\nbuilder.Services.AddControllers()\n    .AddXmlSerializerFormatters(); \u002F\u002F adds application\u002Fxml support\n\n\u002F\u002F Client sends: Accept: application\u002Fxml\n\u002F\u002F → response serialized as XML instead of JSON\n\n\u002F\u002F Force JSON regardless of Accept header:\nbuilder.Services.AddControllers(o =>\n    o.RespectBrowserAcceptHeader = false); \u002F\u002F default: false → always JSON\n\n\u002F\u002F ProducesResponseType + Produces: document and constrain formats\n[HttpGet(\"{id}\")]\n[Produces(\"application\u002Fjson\")]                 \u002F\u002F only produce JSON\n[ProducesResponseType\u003CProduct>(StatusCodes.Status200OK)]\n[ProducesResponseType(StatusCodes.Status404NotFound)]\npublic ActionResult\u003CProduct> Get(int id) { ... }\n```\n\nSwitching to `Newtonsoft.Json` (if you need features STJ doesn't support):\n\n```csharp\nbuilder.Services.AddControllers()\n    .AddNewtonsoftJson(o =>\n        o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore);\n```\n\nCustom output formatter (e.g., CSV):\n\n```csharp\npublic class CsvOutputFormatter : TextOutputFormatter\n{\n    public CsvOutputFormatter()\n    {\n        SupportedMediaTypes.Add(\"text\u002Fcsv\");\n        SupportedEncodings.Add(Encoding.UTF8);\n    }\n\n    protected override bool CanWriteType(Type? type) => type == typeof(IEnumerable\u003CProduct>);\n\n    public override async Task WriteResponseBodyAsync(\n        OutputFormatterWriteContext ctx, Encoding selectedEncoding)\n    {\n        var products = (IEnumerable\u003CProduct>)ctx.Object!;\n        var writer = ctx.HttpContext.Response.BodyWriter;\n        await writer.WriteAsync(\u002F* CSV bytes *\u002F Encoding.UTF8.GetBytes(\"...\"));\n    }\n}\n\nbuilder.Services.AddControllers(o => o.OutputFormatters.Add(new CsvOutputFormatter()));\n```\n\n**Rule of thumb:** Leave content negotiation at its default (JSON) unless clients\nexplicitly need XML or custom formats. Document supported formats with `[Produces]`\nfor accurate OpenAPI specs.\n",{"id":223,"difficulty":43,"q":224,"a":225},"controller-vs-controllerbase","What is the difference between `Controller` and `ControllerBase`?","**`ControllerBase`** provides the HTTP-related helpers (`Ok`, `NotFound`,\n`BadRequest`, `Created`, `File`, etc.) and model binding. It has no Razor View\nsupport.\n\n**`Controller`** inherits `ControllerBase` and adds:\n- `View()`, `PartialView()`, `Json()` for rendering Razor Views\n- `ViewData`, `ViewBag`, `TempData` dictionaries\n- `RedirectToAction`, `RedirectToRoute`\n\n```csharp\n\u002F\u002F Web API — no views needed:\n[ApiController]\n[Route(\"api\u002F[controller]\")]\npublic class ProductsController : ControllerBase\n{\n    [HttpGet]\n    public IActionResult List() => Ok(_products); \u002F\u002F returns JSON\n}\n\n\u002F\u002F MVC — returns Razor views:\npublic class HomeController : Controller\n{\n    public IActionResult Index()\n    {\n        ViewData[\"Title\"] = \"Home\";\n        return View(); \u002F\u002F renders Views\u002FHome\u002FIndex.cshtml\n    }\n\n    public IActionResult Details(int id)\n    {\n        var product = _repo.Find(id);\n        return View(product); \u002F\u002F passes model to view\n    }\n\n    [HttpPost]\n    public IActionResult Create(Product p)\n    {\n        if (!ModelState.IsValid)\n            return View(p); \u002F\u002F re-render form with validation errors\n        _repo.Add(p);\n        return RedirectToAction(nameof(Index));\n    }\n}\n```\n\n**Rule of thumb:** Use `ControllerBase` for Web APIs (returns JSON\u002Fdata). Use\n`Controller` for MVC apps that render HTML views. Never return `View()` from a\nclass that inherits only `ControllerBase` — that method doesn't exist there.\n",{"id":227,"difficulty":35,"q":228,"a":229},"produces-response-type","What is `[ProducesResponseType]` and why does it matter?","`[ProducesResponseType]` is a documentation attribute that declares which HTTP\nstatus codes an action can return and what type the response body has. It is\nused by tools like Swashbuckle (Swagger) to generate accurate OpenAPI specs.\n\n```csharp\n[HttpGet(\"{id:int}\")]\n[ProducesResponseType\u003CProduct>(StatusCodes.Status200OK)]\n[ProducesResponseType(StatusCodes.Status404NotFound)]\n[ProducesResponseType(StatusCodes.Status400BadRequest)]\npublic ActionResult\u003CProduct> Get(int id)\n{\n    if (id \u003C= 0) return BadRequest(\"Id must be positive\");\n    var p = _repo.Find(id);\n    return p is null ? NotFound() : Ok(p);\n}\n\n\u002F\u002F Generic form (C# 11 \u002F .NET 7+) — avoids typeof:\n[ProducesResponseType\u003CProduct>(200)]\n[ProducesResponseType\u003CProblemDetails>(404)]\n\n\u002F\u002F Or use [ProducesDefaultResponseType] for catch-all:\n[ProducesDefaultResponseType(typeof(ProblemDetails))]\n```\n\nWithout `[ProducesResponseType]`, Swagger shows `200 undocumented` which\nmakes client SDK generation guess or produce incorrect models.\n\nApply globally with a convention to avoid repetition:\n\n```csharp\nbuilder.Services.AddControllers(o =>\n    o.Conventions.Add(new ApiExplorerConvention()));\n\u002F\u002F Or use [ProducesResponseType] on the controller class to default for all actions.\n```\n\n**Rule of thumb:** Annotate every API action with `[ProducesResponseType]` for\neach status code it can return. This keeps your OpenAPI spec accurate, improves\nclient SDK generation, and documents the contract for API consumers.\n",{"id":231,"difficulty":43,"q":232,"a":233},"action-return-helpers","What are the common action result helper methods in `ControllerBase`?","`ControllerBase` provides factory methods that create `IActionResult` instances\nwithout manually instantiating result classes:\n\n```csharp\n\u002F\u002F 2xx — success:\nreturn Ok(data);                   \u002F\u002F 200 OK with body\nreturn Created(\"\u002Furi\", data);      \u002F\u002F 201 Created with Location header + body\nreturn CreatedAtAction(nameof(Get), new { id }, data); \u002F\u002F 201 with route-based Location\nreturn Accepted();                 \u002F\u002F 202 Accepted (async operation started)\nreturn NoContent();                \u002F\u002F 204 No Content (successful, no body)\n\n\u002F\u002F 3xx — redirect:\nreturn RedirectToAction(\"Index\");  \u002F\u002F 302 to action\nreturn RedirectToRoute(\"named\");   \u002F\u002F 302 to named route\nreturn LocalRedirect(\"\u002Fpath\");     \u002F\u002F 302, validates local URL (prevents open redirect)\n\n\u002F\u002F 4xx — client error:\nreturn BadRequest(ModelState);     \u002F\u002F 400 with validation errors\nreturn Unauthorized();             \u002F\u002F 401\nreturn Forbid();                   \u002F\u002F 403\nreturn NotFound();                 \u002F\u002F 404\nreturn Conflict(\"Duplicate key\");  \u002F\u002F 409\nreturn UnprocessableEntity(errors);\u002F\u002F 422 Unprocessable Entity\n\n\u002F\u002F 5xx:\nreturn StatusCode(503, \"Maintenance\"); \u002F\u002F arbitrary status with body\n\n\u002F\u002F File responses:\nreturn File(bytes, \"image\u002Fpng\");\nreturn PhysicalFile(\"\u002Fpath\u002Ffile.pdf\", \"application\u002Fpdf\", \"download.pdf\");\nreturn Stream(stream, \"application\u002Foctet-stream\");\n```\n\nThe RFC 7807 Problem Details shortcut:\n\n```csharp\nreturn Problem(\n    title: \"Not Found\",\n    detail: $\"Product {id} does not exist\",\n    statusCode: 404);\n\u002F\u002F → {\"type\":\"...\",\"title\":\"Not Found\",\"status\":404,\"detail\":\"...\"}\n```\n\n**Rule of thumb:** Always use the named helper methods (`Ok`, `NotFound`, etc.)\ninstead of manually setting `Response.StatusCode` — they create properly typed\n`IActionResult` objects and work correctly with the MVC result pipeline.\n",{"id":235,"difficulty":35,"q":236,"a":237},"async-actions","Should controller action methods be async, and what are the best practices?","Actions that perform I\u002FO (database, HTTP, file) should be `async Task\u003CIActionResult>`\nso the server thread is freed while waiting. Synchronous actions are fine only for\ntruly synchronous in-memory operations.\n\n```csharp\n\u002F\u002F Good: async I\u002FO — thread is free while awaiting DB\n[HttpGet]\npublic async Task\u003CActionResult\u003CIEnumerable\u003CProduct>>> List(CancellationToken ct)\n{\n    var products = await _dbContext.Products\n        .AsNoTracking()\n        .ToListAsync(ct); \u002F\u002F non-blocking DB query\n    return Ok(products);\n}\n\n\u002F\u002F Good: accept CancellationToken for client disconnects\n[HttpGet(\"{id:int}\")]\npublic async Task\u003CActionResult\u003CProduct>> Get(int id, CancellationToken ct)\n{\n    var p = await _repo.FindAsync(id, ct);\n    return p is null ? NotFound() : Ok(p);\n}\n\n\u002F\u002F Avoid: blocking async over sync\n[HttpGet(\"bad\")]\npublic IActionResult GetBad()\n{\n    var result = _repo.FindAsync(1).Result; \u002F\u002F blocks a thread — avoid!\n    return Ok(result);\n}\n\n\u002F\u002F Fine: synchronous in-memory ops — no async needed\n[HttpGet(\"version\")]\npublic IActionResult Version() => Ok(\"1.0.0\");\n```\n\nASP.NET Core automatically binds `CancellationToken` parameters from the request\nabort token — when the client disconnects, the token is cancelled, allowing DB\nqueries and HTTP calls to be cancelled.\n\n**Rule of thumb:** Make actions `async Task\u003CIActionResult>` whenever they call\nany I\u002FO. Accept `CancellationToken ct` as the last parameter and forward it to\nevery awaitable call so the server can cancel orphaned requests.\n",{"id":239,"difficulty":35,"q":240,"a":241},"problem-details","What is the Problem Details standard and how does ASP.NET Core implement it?","**Problem Details** (RFC 7807 \u002F RFC 9457) is a standard JSON format for HTTP API\nerror responses. ASP.NET Core 7+ includes built-in Problem Details support via\n`IProblemDetailsService` and the `Problem()` helper method.\n\n```csharp\n\u002F\u002F Enable Problem Details for all error responses:\nbuilder.Services.AddProblemDetails(); \u002F\u002F registers IProblemDetailsService\n\n\u002F\u002F Use it in your exception handler:\napp.UseExceptionHandler();            \u002F\u002F uses ProblemDetails format automatically\n\n\u002F\u002F Return Problem Details from an action:\n[HttpGet(\"{id:int}\")]\npublic ActionResult\u003COrder> Get(int id)\n{\n    var order = _repo.Find(id);\n    if (order is null)\n        return Problem(\n            title:      \"Order not found\",\n            detail:     $\"No order with id {id} exists.\",\n            statusCode: StatusCodes.Status404NotFound,\n            type:       \"https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc9110#section-15.5.5\");\n\n    return Ok(order);\n}\n\n\u002F\u002F [ApiController] wraps 400 responses as ValidationProblemDetails automatically:\n\u002F\u002F POST with invalid body → 400:\n\u002F\u002F {\n\u002F\u002F   \"type\": \"https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc9110#section-15.5.1\",\n\u002F\u002F   \"title\": \"One or more validation errors occurred.\",\n\u002F\u002F   \"status\": 400,\n\u002F\u002F   \"errors\": { \"Name\": [\"The Name field is required.\"] }\n\u002F\u002F }\n\n\u002F\u002F Customize the Problem Details response globally:\nbuilder.Services.AddProblemDetails(opts =>\n{\n    opts.CustomizeProblemDetails = ctx =>\n    {\n        ctx.ProblemDetails.Extensions[\"traceId\"] =\n            ctx.HttpContext.TraceIdentifier; \u002F\u002F add correlation id\n    };\n});\n```\n\n**Rule of thumb:** Enable `AddProblemDetails()` and use the `Problem()` helper for\nall error responses in Web APIs. This gives API consumers a consistent, machine-readable\nerror format across every endpoint, and enables accurate OpenAPI error schema generation.\n",{"id":243,"difficulty":35,"q":244,"a":245},"file-results","How do you return file downloads from a controller action?","`ControllerBase` provides several methods to return file content as a download or\ninline response. Choose the method that matches your data source.\n\n```csharp\n\u002F\u002F 1. Byte array in memory — small files:\n[HttpGet(\"receipt\u002F{id:int}\")]\npublic IActionResult DownloadReceipt(int id)\n{\n    byte[] pdfBytes = _reportService.GeneratePdf(id);\n    return File(\n        fileContents:   pdfBytes,\n        contentType:    \"application\u002Fpdf\",\n        fileDownloadName: $\"receipt-{id}.pdf\"); \u002F\u002F triggers Save As dialog\n}\n\n\u002F\u002F 2. Stream — large files (memory-efficient):\n[HttpGet(\"export\")]\npublic async Task\u003CIActionResult> ExportCsv(CancellationToken ct)\n{\n    var stream = await _exportService.CreateCsvStreamAsync(ct);\n    return File(stream, \"text\u002Fcsv\", \"export.csv\");\n    \u002F\u002F ASP.NET Core disposes the stream after sending\n}\n\n\u002F\u002F 3. Physical file path on disk:\n[HttpGet(\"manual\u002F{filename}\")]\npublic IActionResult GetManual(string filename)\n{\n    var path = Path.Combine(_env.WebRootPath, \"manuals\", filename);\n    if (!System.IO.File.Exists(path))\n        return NotFound();\n\n    return PhysicalFile(\n        physicalPath:    path,\n        contentType:     \"application\u002Fpdf\",\n        fileDownloadName: filename,\n        enableRangeProcessing: true); \u002F\u002F supports partial content (video streaming)\n}\n\n\u002F\u002F 4. Virtual path (served from IFileProvider):\n[HttpGet(\"logo\")]\npublic IActionResult GetLogo() =>\n    VirtualFile(\"~\u002Fimages\u002Flogo.png\", \"image\u002Fpng\");\n\u002F\u002F Note: omit fileDownloadName to serve inline (browser displays instead of downloads)\n```\n\n**Rule of thumb:** Use `File(stream, ...)` for large files to avoid loading them\nfully into memory. Set `enableRangeProcessing: true` for media files. Omit\n`fileDownloadName` to render inline (e.g., images); include it to prompt a download.\n",{"id":247,"difficulty":43,"q":248,"a":249},"redirect-results","How do you perform redirects from a controller action, and what is the difference between the redirect methods?","ASP.NET Core controllers provide several redirect methods, each mapping to a\ndifferent HTTP status code and target type. Choosing the right one prevents\nopen-redirect vulnerabilities and incorrect caching by clients.\n\n```csharp\n\u002F\u002F 301 Permanent redirect — browser\u002Fsearch engines cache forever:\nreturn RedirectPermanent(\"https:\u002F\u002Fnew-site.example.com\");\n\n\u002F\u002F 302 Temporary redirect — default; browser does not cache:\nreturn Redirect(\"https:\u002F\u002Fexternal.example.com\u002Fcheckout\");\n\n\u002F\u002F 302 to another action (safe — uses route generation, no hard-coded URL):\nreturn RedirectToAction(nameof(Index), \"Home\");\nreturn RedirectToAction(nameof(Get), new { id = newId });\n\n\u002F\u002F 301 to another action:\nreturn RedirectToActionPermanent(nameof(Index));\n\n\u002F\u002F 302 to a named route:\nreturn RedirectToRoute(\"order-details\", new { id = order.Id });\n\n\u002F\u002F Local redirect — validates the URL is local (prevents open redirect attacks):\n[HttpPost(\"login\")]\npublic IActionResult Login(string returnUrl)\n{\n    \u002F\u002F Bad: Redirect(returnUrl) — returnUrl could be https:\u002F\u002Fevil.com\n    \u002F\u002F Good: LocalRedirect validates the URL starts with \u002F\n    return LocalRedirect(returnUrl ?? \"\u002Fdashboard\");\n    \u002F\u002F Throws InvalidOperationException if returnUrl is not local\n}\n\n\u002F\u002F 307\u002F308 — preserve HTTP method across redirect (POST stays POST):\nreturn RedirectToActionPreserveMethod(nameof(Create)); \u002F\u002F 307\nreturn RedirectToActionPermanentPreserveMethod(nameof(Create)); \u002F\u002F 308\n```\n\n**Rule of thumb:** Always use `LocalRedirect` when redirecting to a caller-supplied\nURL to prevent open-redirect vulnerabilities. Use `RedirectToAction` instead of\nhard-coded strings — it stays correct when routes change.\n",{"id":251,"difficulty":35,"q":252,"a":253},"consumes-produces-attributes","What do `[Consumes]` and `[Produces]` attributes do on a controller action?","**`[Consumes]`** restricts which request `Content-Type` values an action accepts.\n**`[Produces]`** declares which response `Content-Type` values the action returns.\nBoth affect content negotiation, routing, and OpenAPI documentation.\n\n```csharp\n\u002F\u002F [Consumes]: action only matches requests with Content-Type: application\u002Fjson\n[HttpPost(\"upload-json\")]\n[Consumes(\"application\u002Fjson\")]\npublic IActionResult ImportJson([FromBody] ImportDto dto)\n    => Ok($\"Imported {dto.Count} records\");\n\n\u002F\u002F Separate action for form\u002Fmultipart upload:\n[HttpPost(\"upload-form\")]\n[Consumes(\"multipart\u002Fform-data\")]\npublic IActionResult ImportFile(IFormFile file)\n{\n    \u002F\u002F Handles: Content-Type: multipart\u002Fform-data\n    using var stream = file.OpenReadStream();\n    _importer.Import(stream);\n    return Ok();\n}\n\u002F\u002F Both routes are POST \u002Fupload-* — [Consumes] disambiguates at routing time\n\n\u002F\u002F [Produces]: declares response content type and drives OpenAPI output schema\n[HttpGet(\"{id:int}\")]\n[Produces(\"application\u002Fjson\")]\n[ProducesResponseType\u003CProduct>(StatusCodes.Status200OK)]\n[ProducesResponseType(StatusCodes.Status404NotFound)]\npublic ActionResult\u003CProduct> Get(int id) { ... }\n\n\u002F\u002F [Produces] on the controller: applies to all actions\n[ApiController]\n[Produces(\"application\u002Fjson\")]\n[Route(\"api\u002F[controller]\")]\npublic class ProductsController : ControllerBase { ... }\n\n\u002F\u002F Support multiple response types:\n[Produces(\"application\u002Fjson\", \"application\u002Fxml\")]\npublic IActionResult GetAll() => Ok(_products);\n```\n\n**Rule of thumb:** Use `[Consumes]` when the same route needs to accept different\nbody formats (JSON vs form). Use `[Produces]` to lock down the response format and\nkeep OpenAPI schemas accurate. Together they form the explicit contract for your endpoint.\n",{"description":32},"ASP.NET Core controllers interview questions — IActionResult, model binding, the ApiController attribute, validation, action filters, and content negotiation.","dotnet\u002Faspnet-core\u002Fcontrollers-actions","Controllers & Actions","JPra4UDiuJuizr3UBWsN1FEOk9rGxFFYu3tYwcbVayI",{"id":260,"title":261,"body":262,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":266,"navigation":38,"order":267,"path":268,"questions":269,"questionsCount":102,"related":103,"seo":330,"seoDescription":331,"stem":332,"subtopic":261,"topic":19,"topicSlug":21,"updated":107,"__hash__":333},"qa\u002Fdotnet\u002Faspnet-core\u002Fconfiguration.md","Configuration",{"type":29,"value":263,"toc":264},[],{"title":32,"searchDepth":33,"depth":33,"links":265},[],{},4,"\u002Fdotnet\u002Faspnet-core\u002Fconfiguration",[270,274,278,282,286,290,294,298,302,306,310,314,318,322,326],{"id":271,"difficulty":43,"q":272,"a":273},"configuration-what-is","What is the ASP.NET Core configuration system and how does it work?","The ASP.NET Core **configuration system** aggregates key-value pairs from multiple\n**configuration providers** into a unified `IConfiguration` interface. Providers are\nlayered — later providers override earlier ones for the same key.\n\n```csharp\n\u002F\u002F Default provider order (WebApplication.CreateBuilder):\n\u002F\u002F 1. appsettings.json\n\u002F\u002F 2. appsettings.{Environment}.json (e.g., appsettings.Production.json)\n\u002F\u002F 3. User Secrets (Development only)\n\u002F\u002F 4. Environment variables\n\u002F\u002F 5. Command-line arguments\n\nvar builder = WebApplication.CreateBuilder(args);\n\u002F\u002F builder.Configuration is already populated from the sources above\n\n\u002F\u002F Read a value:\nstring? conn = builder.Configuration[\"ConnectionStrings:DefaultConnection\"];\n\u002F\u002F or via GetConnectionString shortcut:\nstring? conn2 = builder.Configuration.GetConnectionString(\"DefaultConnection\");\n\n\u002F\u002F Read a section:\nIConfigurationSection mailSection = builder.Configuration.GetSection(\"Mail\");\nstring? host = mailSection[\"Host\"];\nint port = mailSection.GetValue\u003Cint>(\"Port\", defaultValue: 587);\n```\n\n`appsettings.json`:\n```json\n{\n  \"ConnectionStrings\": {\n    \"DefaultConnection\": \"Server=.;Database=MyDb;Trusted_Connection=True\"\n  },\n  \"Mail\": {\n    \"Host\": \"smtp.example.com\",\n    \"Port\": 587\n  }\n}\n```\n\n**Rule of thumb:** Never hard-code secrets or environment-specific values in code.\nUse the configuration system so values can be overridden per environment through\nappsettings files, environment variables, or secrets management tools.\n",{"id":275,"difficulty":43,"q":276,"a":277},"configuration-providers","What are the built-in configuration providers and how are they ordered?","ASP.NET Core supports many built-in providers. `WebApplication.CreateBuilder` sets up\nthe default stack automatically; you can customize with `ConfigureAppConfiguration`.\n\n```csharp\n\u002F\u002F Default stack — later entries WIN (override earlier):\n\u002F\u002F appsettings.json → appsettings.{Env}.json → User Secrets → Env vars → CLI args\n\n\u002F\u002F Customizing the stack:\nbuilder.Configuration\n    \u002F\u002F 1. Clear existing providers:\n    .Sources.Clear();\n\nbuilder.Host.ConfigureAppConfiguration((ctx, config) =>\n{\n    config.AddJsonFile(\"appsettings.json\", optional: false, reloadOnChange: true);\n    config.AddJsonFile(\n        $\"appsettings.{ctx.HostingEnvironment.EnvironmentName}.json\",\n        optional: true,\n        reloadOnChange: true);\n\n    if (ctx.HostingEnvironment.IsDevelopment())\n        config.AddUserSecrets\u003CProgram>(); \u002F\u002F safe dev-only secrets\n\n    config.AddEnvironmentVariables(prefix: \"MYAPP_\"); \u002F\u002F MYAPP_Mail__Host → Mail:Host\n\n    config.AddCommandLine(args); \u002F\u002F --Mail:Host=smtp.example.com\n\n    \u002F\u002F Custom: Azure Key Vault (requires Azure.Extensions.AspNetCore.Configuration.Secrets):\n    \u002F\u002F config.AddAzureKeyVault(new Uri(\"https:\u002F\u002Fmyvault.vault.azure.net\u002F\"), new DefaultAzureCredential());\n\n    \u002F\u002F Custom: AWS Secrets Manager, HashiCorp Vault, etc.\n});\n```\n\nEnvironment variable key mapping:\n- Double underscore `__` maps to `:` (section separator).\n- `MYAPP_Mail__Host` → `Mail:Host`\n- `MYAPP_ConnectionStrings__DefaultConnection` → `ConnectionStrings:DefaultConnection`\n\n**Rule of thumb:** For secrets (API keys, connection strings), use User Secrets in\ndevelopment and environment variables \u002F a secrets manager (Azure Key Vault, AWS\nSecrets Manager) in production — never commit secrets to `appsettings.json`.\n",{"id":279,"difficulty":35,"q":280,"a":281},"ioptions","What is the Options pattern (`IOptions\u003CT>`, `IOptionsSnapshot\u003CT>`, `IOptionsMonitor\u003CT>`) and when do you use each?","The **Options pattern** binds a configuration section to a strongly-typed class and\ninjects it via DI. Three interfaces serve different lifetime \u002F reload needs:\n\n- **`IOptions\u003CT>`** — singleton; reads config once at startup; does **not** see changes.\n- **`IOptionsSnapshot\u003CT>`** — scoped; re-reads config **once per request**; sees\n  reloaded values from the next request onward.\n- **`IOptionsMonitor\u003CT>`** — singleton; sees config changes **immediately** via a\n  change-notification callback.\n\n```csharp\n\u002F\u002F POCO:\npublic class MailOptions\n{\n    public const string SectionName = \"Mail\";\n    public string Host { get; set; } = default!;\n    public int Port { get; set; } = 587;\n    public bool UseSsl { get; set; } = true;\n}\n\n\u002F\u002F Register:\nbuilder.Services.Configure\u003CMailOptions>(\n    builder.Configuration.GetSection(MailOptions.SectionName));\n\n\u002F\u002F Inject and use:\npublic class EmailService\n{\n    private readonly MailOptions _opts;\n\n    \u002F\u002F IOptions\u003CT> — fixed at startup (singleton):\n    public EmailService(IOptions\u003CMailOptions> opts) =>\n        _opts = opts.Value;\n\n    \u002F\u002F IOptionsSnapshot\u003CT> — re-read per request (scoped service):\n    public EmailService(IOptionsSnapshot\u003CMailOptions> opts) =>\n        _opts = opts.Value;\n\n    \u002F\u002F IOptionsMonitor\u003CT> — live updates (singleton service):\n    public EmailService(IOptionsMonitor\u003CMailOptions> monitor)\n    {\n        _opts = monitor.CurrentValue;\n        monitor.OnChange(newValue =>\n        {\n            \u002F\u002F Called whenever config changes on disk\n            Console.WriteLine($\"Config changed: Host={newValue.Host}\");\n        });\n    }\n}\n```\n\n| Interface | Lifetime | Sees reloads? | Best for |\n|---|---|---|---|\n| `IOptions\u003CT>` | Singleton | No | Startup-only config, singletons |\n| `IOptionsSnapshot\u003CT>` | Scoped | Per-request | Web requests, feature flags |\n| `IOptionsMonitor\u003CT>` | Singleton | Immediately | Long-running services, background workers |\n\n**Rule of thumb:** Use `IOptions\u003CT>` for configuration that never changes at\nruntime. Use `IOptionsSnapshot\u003CT>` in request-scoped services when you want\nper-request config refresh. Use `IOptionsMonitor\u003CT>` in singletons or background\nservices that need live config updates without restart.\n",{"id":283,"difficulty":35,"q":284,"a":285},"options-validation","How do you validate configuration options at startup to fail fast on bad config?","ASP.NET Core lets you validate Options at startup using Data Annotations,\n`IValidateOptions\u003CT>`, or the fluent `Validate` overload.\n\n```csharp\n\u002F\u002F Data Annotations on the POCO:\npublic class SmtpOptions\n{\n    [Required]\n    public string Host { get; set; } = default!;\n\n    [Range(1, 65535)]\n    public int Port { get; set; }\n\n    [Required, EmailAddress]\n    public string FromAddress { get; set; } = default!;\n}\n\n\u002F\u002F Register with annotation validation + eager validation at startup:\nbuilder.Services\n    .AddOptions\u003CSmtpOptions>()\n    .BindConfiguration(\"Smtp\")\n    .ValidateDataAnnotations()\n    .ValidateOnStart(); \u002F\u002F throws on startup instead of first use!\n\n\u002F\u002F Custom validation:\nbuilder.Services\n    .AddOptions\u003CSmtpOptions>()\n    .BindConfiguration(\"Smtp\")\n    .Validate(opts =>\n    {\n        if (opts.Port == 25 && opts.Host.EndsWith(\".example.com\"))\n            return false; \u002F\u002F invalid combination\n        return true;\n    }, \"Port 25 is not allowed for example.com SMTP servers\")\n    .ValidateOnStart();\n```\n\nWithout `ValidateOnStart()`, validation runs on first access of `.Value`. With it,\nbad config causes the app to refuse to start — a much better failure mode.\n\n**Rule of thumb:** Always add `.ValidateDataAnnotations().ValidateOnStart()` to\nany critical options binding. Failing fast at startup is far better than a\nproduction runtime error when a feature is first exercised.\n",{"id":287,"difficulty":35,"q":288,"a":289},"user-secrets","What are User Secrets and how do you use them in development?","**User Secrets** store sensitive config values (API keys, connection strings) outside\nthe project directory during development, so they are never committed to source\ncontrol. They are stored in `%APPDATA%\\Microsoft\\UserSecrets\\\u003Cid>\\secrets.json`\n(Windows) or `~\u002F.microsoft\u002Fusersecrets\u002F\u003Cid>\u002Fsecrets.json` (Linux\u002FmacOS).\n\n```bash\n# Initialize user secrets for the project (adds \u003CUserSecretsId> to .csproj):\ndotnet user-secrets init\n\n# Set secrets:\ndotnet user-secrets set \"ConnectionStrings:DefaultConnection\" \"Server=.;...\"\ndotnet user-secrets set \"Stripe:SecretKey\" \"sk_test_abc123\"\ndotnet user-secrets set \"Mail:Password\" \"my-smtp-password\"\n\n# List all secrets:\ndotnet user-secrets list\n\n# Remove a secret:\ndotnet user-secrets remove \"Mail:Password\"\n```\n\nThey are automatically loaded in the `Development` environment by\n`WebApplication.CreateBuilder`:\n\n```csharp\n\u002F\u002F Automatically added in Development:\nif (app.Environment.IsDevelopment())\n    builder.Configuration.AddUserSecrets\u003CProgram>();\n\n\u002F\u002F WebApplication.CreateBuilder does this by default — you usually don't need to call it manually.\n\n\u002F\u002F Access normally via IConfiguration:\nvar key = builder.Configuration[\"Stripe:SecretKey\"];\n```\n\nIn production, use environment variables or a secrets manager — User Secrets are\na **development-only** mechanism.\n\n**Rule of thumb:** Use User Secrets for any credential a dev needs locally but\nmust not check in. Never add `secrets.json` to `.gitignore` — it's outside the\nrepo directory by design.\n",{"id":291,"difficulty":43,"q":292,"a":293},"environment-specific-config","How does ASP.NET Core handle environment-specific configuration?","The **hosting environment** (`ASPNETCORE_ENVIRONMENT` env var) controls which\n`appsettings.{Environment}.json` file is loaded. The environment-specific file\n**merges with and overrides** values from `appsettings.json`.\n\n```json\n\u002F\u002F appsettings.json (base — checked into source control):\n{\n  \"Logging\": { \"LogLevel\": { \"Default\": \"Warning\" } },\n  \"FeatureFlags\": { \"NewCheckout\": false },\n  \"ConnectionStrings\": { \"DefaultConnection\": \"\" }\n}\n\n\u002F\u002F appsettings.Development.json (local overrides — can be committed for shared dev):\n{\n  \"Logging\": { \"LogLevel\": { \"Default\": \"Debug\" } },\n  \"FeatureFlags\": { \"NewCheckout\": true }\n}\n\n\u002F\u002F appsettings.Production.json (minimal; secrets come from env vars):\n{\n  \"Logging\": { \"LogLevel\": { \"Default\": \"Error\" } }\n}\n```\n\n```csharp\n\u002F\u002F Check environment in code:\nif (app.Environment.IsDevelopment())\n    app.UseDeveloperExceptionPage();\nelse\n    app.UseExceptionHandler(\"\u002Ferror\");\n\n\u002F\u002F Custom environments:\n\u002F\u002F ASPNETCORE_ENVIRONMENT=Staging → loads appsettings.Staging.json\nif (app.Environment.IsEnvironment(\"Staging\"))\n    EnableStagingFeatures();\n```\n\n`IWebHostEnvironment` (injected via DI) exposes:\n- `EnvironmentName` — the string value\n- `IsDevelopment()`, `IsStaging()`, `IsProduction()` — convenience methods\n- `ContentRootPath`, `WebRootPath`\n\n**Rule of thumb:** Keep `appsettings.json` free of secrets and environment-specific\nvalues. Use `appsettings.{Env}.json` for non-secret environment differences. Put\nsecrets in environment variables or a secrets manager, not in any config file that\ngets committed.\n",{"id":295,"difficulty":35,"q":296,"a":297},"iconfiguration-bind","How do you use `IConfiguration.Bind`, `Get\u003CT>`, and `GetSection` to read config?","`IConfiguration` provides several methods to read values: `GetValue\u003CT>`, `GetSection`,\n`Bind`, and `Get\u003CT>`.\n\n```csharp\n\u002F\u002F appsettings.json:\n\u002F\u002F {\n\u002F\u002F \"App\": { \"Name\": \"MyApp\", \"MaxItems\": 100, \"Tags\": [\"api\",\"web\"] }\n\u002F\u002F }\n\n\u002F\u002F GetValue\u003CT> — read a single value with optional default:\nstring name     = config.GetValue\u003Cstring>(\"App:Name\") ?? \"Default\";\nint maxItems    = config.GetValue\u003Cint>(\"App:MaxItems\", defaultValue: 50);\n\n\u002F\u002F GetSection — get a sub-section (does NOT throw if missing):\nIConfigurationSection appSection = config.GetSection(\"App\");\nstring? sectionName = appSection[\"Name\"]; \u002F\u002F \"MyApp\"\n\n\u002F\u002F Bind — populate an existing object from config:\nvar opts = new AppOptions();\nconfig.GetSection(\"App\").Bind(opts);\n\u002F\u002F opts.Name == \"MyApp\", opts.MaxItems == 100\n\n\u002F\u002F Get\u003CT> — returns a new instance of T bound from the section:\nAppOptions opts2 = config.GetSection(\"App\").Get\u003CAppOptions>()!;\n\n\u002F\u002F Bind arrays:\nstring[] tags = config.GetSection(\"App:Tags\").Get\u003Cstring[]>()!;\n\u002F\u002F tags == [\"api\", \"web\"]\n\npublic class AppOptions\n{\n    public string Name { get; set; } = default!;\n    public int MaxItems { get; set; }\n    public List\u003Cstring> Tags { get; set; } = new();\n}\n```\n\n`GetSection(\"missing\")` returns an empty `IConfigurationSection` (not null) —\ncheck `section.Exists()` before using it:\n\n```csharp\nvar section = config.GetSection(\"FeatureFlags\");\nif (section.Exists())\n    section.Bind(featureFlags);\n```\n\n**Rule of thumb:** Prefer the Options pattern (`IOptions\u003CT>`) over directly calling\n`IConfiguration` in application services — it gives you DI, validation, and reload\nsupport. Use `IConfiguration` directly only in startup code or simple console apps.\n",{"id":299,"difficulty":35,"q":300,"a":301},"configuration-reload","Does ASP.NET Core automatically reload configuration when `appsettings.json` changes?","Yes — by default, `AddJsonFile` is registered with `reloadOnChange: true`, so\n`appsettings.json` and `appsettings.{Env}.json` are re-read when the file changes\non disk (via `FileSystemWatcher`). `IConfiguration` and `IOptionsMonitor\u003CT>` reflect\nthe new values immediately; `IOptionsSnapshot\u003CT>` reflects them on the next request;\n`IOptions\u003CT>` **never** reloads.\n\n```csharp\n\u002F\u002F Reload is on by default — this is what WebApplication.CreateBuilder does:\nconfig.AddJsonFile(\"appsettings.json\", optional: false, reloadOnChange: true);\nconfig.AddJsonFile($\"appsettings.{env}.json\", optional: true, reloadOnChange: true);\n\n\u002F\u002F IOptionsMonitor: subscribes to live changes\npublic class FeatureFlagService\n{\n    private readonly IOptionsMonitor\u003CFeatureOptions> _monitor;\n    public FeatureFlagService(IOptionsMonitor\u003CFeatureOptions> monitor)\n        => _monitor = monitor;\n\n    public bool IsEnabled(string flag)\n        => _monitor.CurrentValue.EnabledFlags.Contains(flag); \u002F\u002F always fresh\n}\n\n\u002F\u002F IOptionsSnapshot: fresh per-request (scoped services)\npublic class RequestHandler\n{\n    private readonly FeatureOptions _opts;\n    public RequestHandler(IOptionsSnapshot\u003CFeatureOptions> opts)\n        => _opts = opts.Value; \u002F\u002F bound once per request scope\n}\n\n\u002F\u002F IOptions: frozen at startup (no reload)\npublic class CachedService\n{\n    private readonly FeatureOptions _opts;\n    public CachedService(IOptions\u003CFeatureOptions> opts)\n        => _opts = opts.Value; \u002F\u002F static — never changes\n}\n```\n\n**Rule of thumb:** Use `IOptionsMonitor\u003CT>` in singletons or background workers\nthat need live config without a restart. In production, use reload carefully for\ncritical values — an invalid config file mid-flight can crash the app or produce\nunexpected behavior.\n",{"id":303,"difficulty":161,"q":304,"a":305},"named-options","What are named options and when do you use them?","**Named options** allow multiple instances of the same options class, each configured\ndifferently. They are useful when you have multiple instances of a service with\ndifferent configuration (e.g., two SMTP servers, multiple HTTP clients).\n\n```csharp\n\u002F\u002F Two SMTP configurations in appsettings.json:\n\u002F\u002F {\n\u002F\u002F \"Smtp\": {\n\u002F\u002F \"Transactional\": { \"Host\": \"smtp1.example.com\", \"Port\": 587 },\n\u002F\u002F \"Marketing\":     { \"Host\": \"smtp2.example.com\", \"Port\": 465 }\n\u002F\u002F }\n\u002F\u002F }\n\n\u002F\u002F Register named options:\nbuilder.Services.Configure\u003CSmtpOptions>(\"Transactional\",\n    builder.Configuration.GetSection(\"Smtp:Transactional\"));\nbuilder.Services.Configure\u003CSmtpOptions>(\"Marketing\",\n    builder.Configuration.GetSection(\"Smtp:Marketing\"));\n\n\u002F\u002F Inject and resolve by name:\npublic class EmailDispatcher\n{\n    private readonly SmtpOptions _transactional;\n    private readonly SmtpOptions _marketing;\n\n    public EmailDispatcher(IOptionsMonitor\u003CSmtpOptions> monitor)\n    {\n        _transactional = monitor.Get(\"Transactional\");\n        _marketing     = monitor.Get(\"Marketing\");\n    }\n\n    public void SendTransactional(string to, string body)\n        => Send(_transactional, to, body);\n\n    public void SendMarketing(string to, string body)\n        => Send(_marketing, to, body);\n}\n```\n\n`IHttpClientFactory` uses named options internally:\n\n```csharp\nbuilder.Services.AddHttpClient(\"github\", client =>\n{\n    client.BaseAddress = new Uri(\"https:\u002F\u002Fapi.github.com\u002F\");\n    client.DefaultRequestHeaders.Add(\"User-Agent\", \"MyApp\");\n});\n\n\u002F\u002F Inject named client:\nvar client = httpClientFactory.CreateClient(\"github\");\n```\n\n**Rule of thumb:** Use named options when you need multiple configurations for the\nsame options type — commonly for HTTP clients, SMTP servers, external service\nendpoints, or feature flag namespaces.\n",{"id":307,"difficulty":161,"q":308,"a":309},"post-configure","What is `PostConfigure` and how does it differ from `Configure`?","**`Configure\u003CT>`** sets up options values. **`PostConfigure\u003CT>`** runs *after all\n`Configure` registrations* for the same type — it is the last step and can override\nor validate values set by earlier `Configure` calls or by third-party libraries.\n\n```csharp\n\u002F\u002F Scenario: override a value set by an external library's Configure call\n\n\u002F\u002F External library registers its defaults:\nbuilder.Services.Configure\u003CCacheOptions>(opts =>\n{\n    opts.MaxSize = 100;\n    opts.Expiry  = TimeSpan.FromMinutes(10);\n});\n\n\u002F\u002F Your code runs AFTER — PostConfigure:\nbuilder.Services.PostConfigure\u003CCacheOptions>(opts =>\n{\n    \u002F\u002F Runs after ALL Configure calls for CacheOptions\n    if (opts.MaxSize > 500)\n        opts.MaxSize = 500; \u002F\u002F enforce a cap regardless of what lib set\n});\n\n\u002F\u002F PostConfigureAll — applies to ALL named instances:\nbuilder.Services.PostConfigureAll\u003CSmtpOptions>(opts =>\n    opts.UseSsl = true); \u002F\u002F enforce SSL on every named SMTP config\n```\n\nExecution order for options pipeline:\n1. All `Configure\u003CT>` calls (in registration order)\n2. All `PostConfigure\u003CT>` calls (in registration order)\n3. `IValidateOptions\u003CT>` validators\n\n```csharp\n\u002F\u002F Confirm ordering with a simple example:\nbuilder.Services.Configure\u003CMyOpts>(o => o.Value = \"first\");\nbuilder.Services.Configure\u003CMyOpts>(o => o.Value = \"second\");\nbuilder.Services.PostConfigure\u003CMyOpts>(o => o.Value = \"post\"); \u002F\u002F wins\n\u002F\u002F Result: opts.Value == \"post\"\n```\n\n**Rule of thumb:** Use `PostConfigure` when you need to enforce global constraints\non options that third-party libraries configure, or when you need a guaranteed\n\"last word\" on a value regardless of other configuration sources.\n",{"id":311,"difficulty":35,"q":312,"a":313},"config-in-non-web","How do you use the ASP.NET Core configuration system in a console app or background service?","The configuration system is part of `Microsoft.Extensions.Configuration` and works\nin any .NET app — not just ASP.NET Core. Use `Host.CreateDefaultBuilder` or the\nnewer `Host.CreateApplicationBuilder` to get the same provider stack.\n\n```csharp\n\u002F\u002F Worker service \u002F console app:\nusing Microsoft.Extensions.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\n\nvar host = Host.CreateDefaultBuilder(args)\n    .ConfigureAppConfiguration((ctx, config) =>\n    {\n        \u002F\u002F Same providers as WebApplication.CreateBuilder:\n        config.AddJsonFile(\"appsettings.json\", optional: false, reloadOnChange: true);\n        config.AddJsonFile($\"appsettings.{ctx.HostingEnvironment.EnvironmentName}.json\",\n            optional: true);\n        config.AddEnvironmentVariables();\n        if (ctx.HostingEnvironment.IsDevelopment())\n            config.AddUserSecrets\u003CProgram>();\n    })\n    .ConfigureServices((ctx, services) =>\n    {\n        services.Configure\u003CWorkerOptions>(ctx.Configuration.GetSection(\"Worker\"));\n        services.AddHostedService\u003CMyWorker>();\n    })\n    .Build();\n\nawait host.RunAsync();\n\n\u002F\u002F Background service receives options via DI:\npublic class MyWorker : BackgroundService\n{\n    private readonly WorkerOptions _opts;\n    public MyWorker(IOptions\u003CWorkerOptions> opts) => _opts = opts.Value;\n\n    protected override async Task ExecuteAsync(CancellationToken ct)\n    {\n        while (!ct.IsCancellationRequested)\n        {\n            DoWork(_opts.IntervalSeconds);\n            await Task.Delay(TimeSpan.FromSeconds(_opts.IntervalSeconds), ct);\n        }\n    }\n}\n```\n\n**Rule of thumb:** Use `Host.CreateDefaultBuilder` or `Host.CreateApplicationBuilder`\nin console apps and background services to get the same configuration, logging, and\nDI infrastructure as ASP.NET Core — avoid reinventing it with manual provider setup.\n",{"id":315,"difficulty":161,"q":316,"a":317},"iconfigure-options","What is `IConfigureOptions\u003CT>` and how does it differ from `services.Configure\u003CT>`?","**`IConfigureOptions\u003CT>`** is an interface that lets you encapsulate options\nconfiguration logic in a dedicated class, which is itself resolved from DI.\nThis is useful when the configuration logic requires services (e.g., reading from\na database or calling an API) that are not available during the `Configure` delegate.\n\n```csharp\n\u002F\u002F Plain Configure\u003CT> — closure-based, no DI services available:\nbuilder.Services.Configure\u003CJwtOptions>(opts =>\n{\n    opts.Secret = builder.Configuration[\"Jwt:Secret\"]!;\n    \u002F\u002F Cannot inject ISomeService here — DI is not yet built\n});\n\n\u002F\u002F IConfigureOptions\u003CT> — runs after DI is built; can inject services:\npublic class JwtOptionsConfigurator : IConfigureOptions\u003CJwtOptions>\n{\n    private readonly IConfiguration _config;\n    private readonly ISecretVaultService _vault;\n\n    \u002F\u002F Dependencies resolved from the built DI container:\n    public JwtOptionsConfigurator(IConfiguration config, ISecretVaultService vault)\n    {\n        _config = config;\n        _vault  = vault;\n    }\n\n    public void Configure(JwtOptions options)\n    {\n        options.Issuer   = _config[\"Jwt:Issuer\"]!;\n        options.Audience = _config[\"Jwt:Audience\"]!;\n        \u002F\u002F Fetch secret from vault — possible because this runs post-DI-build:\n        options.Secret   = _vault.GetSecret(\"jwt-signing-key\");\n    }\n}\n\n\u002F\u002F Register the configurator as a DI service:\nbuilder.Services.AddSingleton\u003CIConfigureOptions\u003CJwtOptions>, JwtOptionsConfigurator>();\n\n\u002F\u002F IPostConfigureOptions\u003CT> — same pattern but runs after all Configure calls:\npublic class JwtPostConfigurator : IPostConfigureOptions\u003CJwtOptions>\n{\n    public void PostConfigure(string? name, JwtOptions options)\n    {\n        if (string.IsNullOrEmpty(options.Secret))\n            throw new InvalidOperationException(\"JWT secret must be configured\");\n    }\n}\nbuilder.Services.AddSingleton\u003CIPostConfigureOptions\u003CJwtOptions>, JwtPostConfigurator>();\n```\n\n**Rule of thumb:** Use `services.Configure\u003CT>` for simple config binding from\n`IConfiguration`. Use `IConfigureOptions\u003CT>` when configuration requires injected\nservices (vault clients, DB lookups, computed values) — it runs after the container\nis fully built and gives you access to the complete DI graph.\n",{"id":319,"difficulty":161,"q":320,"a":321},"azure-key-vault-config","How do you integrate Azure Key Vault as a configuration provider in ASP.NET Core?","Azure Key Vault is added as a configuration provider using\n`Azure.Extensions.AspNetCore.Configuration.Secrets`. Secrets are loaded alongside\n`appsettings.json` and accessible through the standard `IConfiguration` interface.\n\n```csharp\n\u002F\u002F Package: Azure.Extensions.AspNetCore.Configuration.Secrets\n\u002F\u002F          Azure.Identity\n\nusing Azure.Identity;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F Add Key Vault as a configuration source (production pattern):\nif (!builder.Environment.IsDevelopment())\n{\n    var keyVaultUri = new Uri(builder.Configuration[\"KeyVault:Uri\"]!);\n\n    \u002F\u002F DefaultAzureCredential: tries Managed Identity, then az login, etc.\n    builder.Configuration.AddAzureKeyVault(\n        keyVaultUri,\n        new DefaultAzureCredential());\n}\n\n\u002F\u002F Secrets are available via IConfiguration using -- as the key separator:\n\u002F\u002F Key Vault secret \"ConnectionStrings--DefaultConnection\"\n\u002F\u002F   → accessible as Configuration[\"ConnectionStrings:DefaultConnection\"]\n\n\u002F\u002F Access via Options pattern (preferred):\nbuilder.Services.Configure\u003CDbOptions>(builder.Configuration.GetSection(\"ConnectionStrings\"));\n\n\u002F\u002F Key Vault secret naming conventions:\n\u002F\u002F \"Mail--Host\"     → Mail:Host\n\u002F\u002F \"Jwt--Secret\"    → Jwt:Secret\n\u002F\u002F Named version:   \"Mail--Smtp1--Host\" → Mail:Smtp1:Host\n\n\u002F\u002F Prefix filtering — only load secrets starting with \"MyApp-\":\nbuilder.Configuration.AddAzureKeyVault(\n    keyVaultUri,\n    new DefaultAzureCredential(),\n    new AzureKeyVaultConfigurationOptions\n    {\n        Manager = new KeyVaultSecretManager() \u002F\u002F custom: override to filter\u002Frename keys\n    });\n```\n\nKey Vault secrets are fetched once at startup (no reload by default). Enable polling:\n\n```csharp\nnew AzureKeyVaultConfigurationOptions\n{\n    ReloadInterval = TimeSpan.FromMinutes(30) \u002F\u002F re-fetch secrets every 30 min\n}\n```\n\n**Rule of thumb:** In production, replace User Secrets and env-var secrets with\nAzure Key Vault. Use `DefaultAzureCredential` so the same code works with Managed\nIdentity in Azure and with `az login` locally. Never store Key Vault URIs or\ncredentials in `appsettings.json`.\n",{"id":323,"difficulty":35,"q":324,"a":325},"config-in-tests","How do you override configuration values in integration tests?","When using `WebApplicationFactory\u003CT>` for integration tests, you can override\nconfiguration via `ConfigureAppConfiguration` in a custom factory, or by setting\nenvironment variables before the test host starts.\n\n```csharp\nusing Microsoft.AspNetCore.Mvc.Testing;\nusing Microsoft.Extensions.Configuration;\n\n\u002F\u002F Custom factory that overrides configuration for tests:\npublic class TestWebApplicationFactory : WebApplicationFactory\u003CProgram>\n{\n    protected override void ConfigureWebHost(IWebHostBuilder builder)\n    {\n        builder.ConfigureAppConfiguration((ctx, config) =>\n        {\n            \u002F\u002F Add in-memory config — overrides appsettings.json (added later = wins):\n            config.AddInMemoryCollection(new Dictionary\u003Cstring, string?>\n            {\n                [\"ConnectionStrings:DefaultConnection\"] = \"DataSource=:memory:\",\n                [\"Mail:Host\"]     = \"localhost\",\n                [\"Mail:Port\"]     = \"1025\",\n                [\"FeatureFlags:NewCheckout\"] = \"true\",\n                [\"Jwt:Secret\"]    = \"test-only-signing-key-minimum-32-chars!!\"\n            });\n        });\n\n        \u002F\u002F Also swap out services for test doubles:\n        builder.ConfigureServices(services =>\n        {\n            \u002F\u002F Replace real DB with in-memory:\n            services.RemoveAll\u003CDbContextOptions\u003CAppDb>>();\n            services.AddDbContext\u003CAppDb>(o => o.UseInMemoryDatabase(\"TestDb\"));\n        });\n    }\n}\n\n\u002F\u002F Test class:\npublic class OrdersApiTests : IClassFixture\u003CTestWebApplicationFactory>\n{\n    private readonly HttpClient _client;\n\n    public OrdersApiTests(TestWebApplicationFactory factory)\n        => _client = factory.CreateClient();\n\n    [Fact]\n    public async Task GetOrders_ReturnsOk()\n    {\n        var response = await _client.GetAsync(\"\u002Fapi\u002Forders\");\n        response.EnsureSuccessStatusCode();\n    }\n}\n```\n\n**Rule of thumb:** Use `AddInMemoryCollection` inside `ConfigureAppConfiguration`\nto inject test-specific values — it is the last provider added and therefore wins.\nKeep test config minimal: only override values that differ from defaults or would\ncause the test to contact real external services.\n",{"id":327,"difficulty":161,"q":328,"a":329},"change-token","What is `IChangeToken` and how does it relate to configuration reload notifications?","**`IChangeToken`** is the low-level primitive that the configuration system uses to\nsignal when a source has changed. `IConfiguration.GetReloadToken()` returns a change\ntoken that fires once when any provider triggers a reload. You can use it to react\nto config changes in code that does not have access to `IOptionsMonitor\u003CT>`.\n\n```csharp\n\u002F\u002F Low-level change-token subscription:\nvoid WatchForChanges(IConfiguration config)\n{\n    \u002F\u002F GetReloadToken returns a one-shot token — must re-register after each fire:\n    ChangeToken.OnChange(\n        changeTokenProducer: () => config.GetReloadToken(),\n        changeTokenConsumer: () =>\n        {\n            Console.WriteLine(\"Configuration reloaded at \" + DateTime.UtcNow);\n            \u002F\u002F Re-read any cached config values here\n        });\n}\n\n\u002F\u002F IOptionsMonitor\u003CT> uses the same mechanism internally — prefer it for options:\npublic class FeatureFlagCache\n{\n    private HashSet\u003Cstring> _enabledFlags;\n\n    public FeatureFlagCache(IOptionsMonitor\u003CFeatureOptions> monitor)\n    {\n        _enabledFlags = BuildCache(monitor.CurrentValue);\n\n        \u002F\u002F OnChange callback fires whenever config changes on disk:\n        monitor.OnChange(opts =>\n        {\n            _enabledFlags = BuildCache(opts); \u002F\u002F rebuild the local cache\n            Console.WriteLine(\"Feature flags reloaded\");\n        });\n    }\n\n    private static HashSet\u003Cstring> BuildCache(FeatureOptions opts)\n        => opts.EnabledFlags.ToHashSet(StringComparer.OrdinalIgnoreCase);\n\n    public bool IsEnabled(string flag) => _enabledFlags.Contains(flag);\n}\n\n\u002F\u002F Custom IChangeToken implementation (e.g., for a DB-backed config provider):\npublic class DbChangeToken : IChangeToken\n{\n    private readonly CancellationToken _token;\n    public DbChangeToken(CancellationToken token) => _token = token;\n\n    public bool HasChanged => _token.IsCancellationRequested;\n    public bool ActiveChangeCallbacks => true;\n\n    public IDisposable RegisterChangeCallback(Action\u003Cobject?> callback, object? state)\n        => _token.Register(callback, state);\n}\n```\n\n**Rule of thumb:** Use `IOptionsMonitor\u003CT>.OnChange` for most config-reload\nscenarios — it is the safe, high-level API. Drop down to `IChangeToken` directly\nonly when building a custom configuration provider or when you need to watch for\nchanges outside the Options pattern.\n",{"description":32},"ASP.NET Core configuration interview questions — IConfiguration, IOptions, IOptionsMonitor, environment variables, user secrets, and startup validation.","dotnet\u002Faspnet-core\u002Fconfiguration","U6tyViKfpJg8lm-Lcl1ez7oNVWLpnBW8qjECMvBsdss",1782244097478]