[{"data":1,"prerenderedAt":110},["ShallowReactive",2],{"qa-\u002Fdotnet\u002Faspnet-core\u002Fcontrollers-actions":3},{"page":4,"siblings":94,"blog":107},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":84,"related":85,"seo":86,"seoDescription":87,"stem":88,"subtopic":89,"topic":90,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fdotnet\u002Faspnet-core\u002Fcontrollers-actions.md","Controllers Actions",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md",".NET Core","dotnet",{},true,3,"\u002Fdotnet\u002Faspnet-core\u002Fcontrollers-actions",[23,28,32,36,40,44,48,52,56,60,64,68,72,76,80],{"id":24,"difficulty":25,"q":26,"a":27},"controller-what-is","easy","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":29,"difficulty":25,"q":30,"a":31},"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":33,"difficulty":14,"q":34,"a":35},"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":37,"difficulty":14,"q":38,"a":39},"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":41,"difficulty":14,"q":42,"a":43},"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":45,"difficulty":14,"q":46,"a":47},"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":49,"difficulty":14,"q":50,"a":51},"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":53,"difficulty":25,"q":54,"a":55},"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":57,"difficulty":14,"q":58,"a":59},"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":61,"difficulty":25,"q":62,"a":63},"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":65,"difficulty":14,"q":66,"a":67},"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":69,"difficulty":14,"q":70,"a":71},"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":73,"difficulty":14,"q":74,"a":75},"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":77,"difficulty":25,"q":78,"a":79},"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":81,"difficulty":14,"q":82,"a":83},"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",15,null,{"description":11},"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","ASP.NET Core","aspnet-core","2026-06-23","JPra4UDiuJuizr3UBWsN1FEOk9rGxFFYu3tYwcbVayI",[95,99,102,103],{"subtopic":96,"path":97,"order":98},"Middleware","\u002Fdotnet\u002Faspnet-core\u002Fmiddleware",1,{"subtopic":100,"path":101,"order":12},"Routing","\u002Fdotnet\u002Faspnet-core\u002Frouting",{"subtopic":89,"path":21,"order":20},{"subtopic":104,"path":105,"order":106},"Configuration","\u002Fdotnet\u002Faspnet-core\u002Fconfiguration",4,{"path":108,"title":109},"\u002Fblog\u002Fdotnet-aspnet-core-controllers-actions","ASP.NET Core Controllers, Model Binding, and Action Filters",1782244118323]