ASP.NET MVC 5 interview questions

10 questions on legacy ASP.NET MVC 5: the request lifecycle, routing and areas, filter order, ViewBag and TempData, anti-forgery, overposting, and the async deadlock trap.

01ASP.NET MVC 5Foundation

MVC 5 request lifecycle

Answer

  • In MVC 5 the request hits IIS and the ASP.NET System.Web pipeline before MVC runs.
  • UrlRoutingModule matches the URL to a route entry and selects an MvcRouteHandler / MvcHandler.
  • MvcHandler asks the controller factory to create the controller, then builds a ControllerContext.
  • The filter pipeline runs (authentication, authorization, action, result, exception filters as applicable).
  • The action invoker selects the action method, binds parameters, and invokes it.
  • The action returns an ActionResult. Executing that result writes the HTTP response (view render, redirect, JSON, file).
  • View engines (RazorViewEngine) locate and render views when the result is a ViewResult.
  • This lifecycle is System.Web-specific. ASP.NET Core replaces it with middleware and endpoint routing.
  • Knowing the order helps debug why OnActionExecuting ran but the view never rendered (result filters, exceptions, early results).
  • Child actions and Html.Action re-enter action invocation during view rendering, nested in the same request.
Flow diagram: MVC 5 request path
C#
// RouteConfig.cs (App_Start) routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
02ASP.NET MVC 5Foundation

MVC 5 routing and areas

Answer

  • Conventional routes in RouteConfig map URL patterns to {controller}, {action}, and optional {id} values.
  • Attribute routing uses [Route] and [RoutePrefix] on controllers and actions. Enable with routes.MapMvcAttributeRoutes().
  • Areas group controllers under a URL segment and AreaRegistration classes. RegisterAllAreas discovers them.
  • Registration order matters when you mix attribute routes, areas, and a catch-all default route.
  • Recommended order: MapMvcAttributeRoutes, then AreaRegistration.RegisterAllAreas, then MapRoute for conventional defaults.
  • Templates that call RegisterAllAreas in Application_Start before RouteConfig often hide attribute routes behind area/conventional matches.
  • Route constraints (regex, int) reject mismatched segments before the action runs.
  • Generate outbound URLs with UrlHelper / Html.ActionLink so link text stays correct when routes change.
  • Ambiguous actions (same verb and template) throw at runtime. Keep templates and action names distinct.
  • ASP.NET Core attribute routing is similar in spirit but configured through endpoint routing, not System.Web RouteCollection the same way.
Flow diagram: Route matching order
C#
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapMvcAttributeRoutes(); AreaRegistration.RegisterAllAreas(); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } [RoutePrefix("api/orders")] public class OrdersController : Controller { [HttpGet, Route("{id:int}")] public ActionResult Get(int id) => Json(new { id }, JsonRequestBehavior.AllowGet); }
03ASP.NET MVC 5Foundation

MVC 5 filters and order

Answer

  • MVC 5 filter kinds include IAuthenticationFilter, authorization filters, action filters, result filters, and exception filters.
  • Happy path order: authentication, authorization, OnActionExecuting, action, OnActionExecuted, OnResultExecuting, result, OnResultExecuted.
  • Exception filters run when an unhandled exception escapes the action or earlier filter stages.
  • If authorization sets a Result (challenge or forbid), MVC executes that result and skips the action.
  • IActionFilter / IResultFilter have sync and async interfaces. Pick one style. Do not block on .Result inside filters.
  • Global filters register in FilterConfig. Filter attributes can also sit on controllers or actions.
  • IOrderedFilter.Order and scope (global, controller, action) decide order among filters of the same kind.
  • OutputCache and custom action filters that run before the action can short-circuit by setting filterContext.Result.
  • Do not confuse MVC 5 filter interfaces with ASP.NET Core IAsyncActionFilter in Microsoft.AspNetCore.Mvc.Filters. Namespaces differ.
  • Authorization filters should not do heavy work that belongs in the action. Keep them focused on access decisions.
Flow diagram: Filter stages (happy path)
C#
public sealed class TimingAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { context.HttpContext.Items["t0"] = Environment.TickCount; } public override void OnActionExecuted(ActionExecutedContext context) { var t0 = (int)context.HttpContext.Items["t0"]; context.HttpContext.Response.Headers["X-Action-Ms"] = (Environment.TickCount - t0).ToString(); } } [Timing] public ActionResult Index() => View();
04ASP.NET MVC 5Foundation

ViewBag, ViewData, and TempData

Answer

  • ViewData is a dictionary that carries values from controller to view for the current request only.
  • ViewBag is a dynamic wrapper over the same ViewData dictionary. ViewBag.Title and ViewData["Title"] share storage.
  • Neither ViewData nor ViewBag survives a redirect. They are gone when the next request starts.
  • TempData survives into the next request. The default provider stores values in session until read.
  • Use TempData for one-shot messages after RedirectToAction (Post-Redirect-Get). It is not a general cache.
  • Reading a TempData key deletes it by default. TempData.Peek reads without delete. TempData.Keep marks keys to retain.
  • If session is disabled, TempData providers that need session will not work. Configure session before relying on it.
  • Prefer typed view models over stuffing many keys into ViewBag. Dynamics hide typos until runtime.
  • ASP.NET Core keeps ViewData/ViewBag/TempData concepts, but cookie-based TempData providers are also available.
  • Do not put large payloads in TempData. Session size and serialization costs grow quickly.
Flow diagram: Where the data lives
C#
public ActionResult Save(OrderInput input) { if (!ModelState.IsValid) return View(input); // save... TempData["Status"] = "Order saved."; return RedirectToAction("Index"); } // Index.cshtml @TempData["Status"]
05ASP.NET MVC 5Foundation

MVC 5 anti-forgery (CSRF)

Answer

  • Cookie-authenticated browsers send cookies on cross-site POSTs. CSRF exploits that automatic cookie send.
  • Anti-forgery tokens tie a POST to a form (or AJAX call) that your server issued to that session.
  • @Html.AntiForgeryToken() emits a hidden field. [ValidateAntiForgeryToken] checks the field against the cookie token.
  • Validate on cookie-auth state changes (POST, PUT, DELETE). Safe GETs must not change server state.
  • AJAX must send the token in the form field or a header your app validates (often RequestVerificationToken).
  • IgnoreAntiforgeryToken is for rare endpoints that are not cookie-auth browser forms (and must be justified).
  • APIs using only Bearer tokens in Authorization headers are not classic cookie CSRF, but cookie+API hybrids still need care.
  • ASP.NET Core uses [ValidateAntiForgeryToken] / AutoValidateAntiforgeryToken and antiforgery services with similar goals.
  • Missing tokens produce HTTP 400 (or a framework-specific antiforgery exception). Log them; they can be attacks or broken forms.
  • Never disable antiforgery globally to "fix" AJAX. Fix the client to send the token instead.
Sequence diagram: Token round trip
C#
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(ProductInput input) { if (!ModelState.IsValid) return View(input); // update... return RedirectToAction("Details", new { id = input.Id }); } // In the form: // @using (Html.BeginForm()) { @Html.AntiForgeryToken(); ... }
06ASP.NET MVC 5Foundation

Overposting / mass assignment

Answer

  • Model binding sets public properties from the request. Attackers can post fields that never appeared in your HTML form.
  • That is overposting / mass assignment: IsAdmin, Balance, OwnerId, and similar get set if they exist on the bound type.
  • Bind a dedicated input model that only contains editable fields, then copy those onto the entity.
  • [Bind(Include = "Name,Email")] on MVC 5 action parameters can whitelist fields, but a separate input type is clearer.
  • On edit, load the entity from the database by id, then assign only allowed properties from the input model.
  • Do not bind straight to EF entities that mix editable columns with security or audit columns.
  • API JSON overposting is the same risk. [ApiController] does not block extra properties from setting matching setters.
  • Prefer init-only or private setters on properties the client must never set, when the type is shared.
  • Review automated scanners that POST extra form fields during security tests.
  • Authorization checks (can this user edit this row?) are separate from overposting. You need both.
Flow diagram: Bind only what the user may change
C#
public class ProductEditInput { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } // no IsFeatured / CreatedBy here } [HttpPost, ValidateAntiForgeryToken] public ActionResult Edit(ProductEditInput input) { var entity = db.Products.Find(input.Id); if (entity == null) return HttpNotFound(); entity.Name = input.Name; entity.Price = input.Price; db.SaveChanges(); return RedirectToAction("Details", new { id = entity.Id }); }
07ASP.NET MVC 5Foundation

MVC 5 ActionResult types

Answer

  • ActionResult is the abstract base. MVC executes it after the action returns to produce the HTTP response.
  • ViewResult and PartialViewResult render Razor (or other) views. RedirectToRouteResult / RedirectToActionResult send 302/301.
  • JsonResult serializes an object. FileResult / FileStreamResult / FilePathResult send file payloads.
  • HttpNotFoundResult yields 404. HttpStatusCodeResult sets an arbitrary status. ContentResult writes plain text.
  • After a successful POST, RedirectToAction (PRG) stops a browser refresh from replaying the POST.
  • JsonResult rejects GET by default in MVC 5. Pass JsonRequestBehavior.AllowGet only when the JSON is safe for GET.
  • Helper methods (View(), RedirectToAction(), Json()) return ActionResult instances; they do not write immediately.
  • Returning a view name string is sugar for ViewResult. Prefer explicit ActionResult for clarity in interviews and reviews.
  • ASP.NET Core keeps the ActionResult idea but uses different concrete types and ObjectResult for API payloads.
  • Do not mix writing directly to Response.Output with returning a ViewResult. Pick one response path.
Flow diagram: Action returns a result object
C#
public ActionResult Details(int id) { var product = db.Products.Find(id); if (product == null) return HttpNotFound(); return View(product); } public ActionResult Export(int id) { var bytes = BuildPdf(id); return File(bytes, "application/pdf", "order.pdf"); }
08ASP.NET MVC 5Foundation

Child actions and Html.Action

Answer

  • Html.Action and Html.RenderAction invoke another action and inline its output while the parent view renders.
  • RenderAction writes directly to the response stream. Action returns an MvcHtmlString you can compose.
  • Mark reusable actions [ChildActionOnly] so they cannot be reached as a normal public URL.
  • Child actions share the parent request and ViewData dictionary context; they are not a separate HTTP call.
  • Expensive child actions (DB calls per partial) slow the whole page. Cache fragments when needed.
  • Use a partial view with a model from the parent when you only share markup. Use a child action when the fragment needs its own controller logic.
  • Child actions do not run the full page lifecycle the same way as a top-level request. Avoid relying on layout assumptions.
  • ASP.NET Core dropped child actions. Use view components or partial views instead when migrating.
  • Nested child actions multiply latency. Keep the graph shallow.
  • Authorization on child actions still matters if they ever become reachable; ChildActionOnly is the first gate.
Flow diagram: Parent view calls a child action
C#
[ChildActionOnly] public PartialViewResult CartSummary() { var count = cart.CountItems(User.Identity.Name); return PartialView("_CartSummary", count); } // In the layout: // @Html.Action("CartSummary", "Cart")
09ASP.NET MVC 5Foundation

MVC 5 vs ASP.NET Core MVC

Answer

  • MVC 5 depends on System.Web and typically hosts on IIS. ASP.NET Core MVC runs on Kestrel (often behind IIS or nginx) and is cross-platform.
  • ASP.NET Core builds middleware and dependency injection into the host. MVC 5 often adds OWIN and a third-party container by hand.
  • MVC 5 uses System.Web.HttpContext. ASP.NET Core uses Microsoft.AspNetCore.Http.HttpContext. Types do not move unchanged.
  • Filters, model binding, and ActionResult feel familiar, but namespaces, packages, and registration APIs differ.
  • Core uses endpoint routing and the middleware pipeline instead of the System.Web module pipeline.
  • Configuration moves from Web.config to appsettings.json, environment variables, and the Options pattern.
  • Child actions exist in MVC 5. Core replaces them with view components.
  • Greenfield work usually targets ASP.NET Core. MVC 5 still appears in brownfield apps and migration plans.
  • Migration is incremental for many teams: reverse proxy in front, then port projects, then move auth and session carefully.
  • Do not assume NuGet packages built for System.Web work on Core. Check for AspNetCore-targeted replacements.
Flow diagram: Hosting models
C#
// MVC 5 public class HomeController : System.Web.Mvc.Controller { public ActionResult Index() => View(); } // ASP.NET Core public class HomeController : Microsoft.AspNetCore.Mvc.Controller { public IActionResult Index() => View(); }
10ASP.NET MVC 5Advanced

MVC 5 async and sync-over-async

Answer

  • Use async Task<ActionResult> and await I/O end to end in MVC 5 actions.
  • On classic ASP.NET (System.Web), .Result or .Wait() on a task can deadlock the request.
  • Why: the blocked thread holds the request synchronization context, and the await continuation needs that context to finish.
  • ASP.NET Core does not install that same request synchronization context, so this exact deadlock is an MVC 5 / System.Web issue.
  • ConfigureAwait(false) belongs in library code that does not need HttpContext. In controllers, await and do not block.
  • After some awaits, HttpContext.Current can be null if the continuation lost the request context. Prefer Controller members (User, Url).
  • Capture values you need from HttpContext before an await if you must pass them into ConfigureAwait(false) library calls.
  • Async void is for event handlers only. Actions and filters should return Task.
  • Entity Framework 6 and HttpClient both expose async methods. Prefer those over sync wrappers.
  • If a legacy API is sync-only, offloading with Task.Run does not fix thread-pool starvation under load; treat it as tech debt.
Flow diagram: Blocked request context
C#
// Bad: can deadlock under the ASP.NET sync context public ActionResult Bad() { var data = GetDataAsync().Result; return View(data); } // Good public async Task<ActionResult> Good() { var data = await GetDataAsync(); return View(data); } private static async Task<string> GetDataAsync() { await Task.Delay(50); return "ok"; }
Sources and further reading