01 ASP.NET MVC 5 Foundation
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 }
);
02 ASP.NET MVC 5 Foundation
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 );
}
03 ASP.NET MVC 5 Foundation
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 ();
04 ASP.NET MVC 5 Foundation
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" ]
05 ASP.NET MVC 5 Foundation
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(); ... }
06 ASP.NET MVC 5 Foundation
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 });
}
07 ASP.NET MVC 5 Foundation
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" );
}
08 ASP.NET MVC 5 Foundation
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")
09 ASP.NET MVC 5 Foundation
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 ();
}
10 ASP.NET MVC 5 Advanced
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" ;
}