MVC

What the model, view and controller each do, how an action returns a view, and where the view engine looks for the file.

Syntaxreturn View(model); // renders Views/{Controller}/{Action}.cshtml

MVC is a way of splitting the code that builds a web page into three parts. The letters stand for model, view and controller.

A controller is a class that handles requests. Each public method on it is an action, and one action handles one kind of request. A view is a file of HTML with holes in it, ending in .cshtml. A model, in this context, is one object holding everything that view needs to fill its holes. The view only reads the model. It never fetches data of its own.

Turning it on

builder.Services.AddControllersWithViews() registers the services MVC needs, including the engine that renders .cshtml files. app.MapControllerRoute connects URLs to actions. The usual pattern, {controller=Home}/{action=Index}/{id?}, sends /products/detail/4 to the Detail action of ProductsController with id set to 4.

Returning a view

An action returns View(model), which builds a ViewResult that the framework then renders. The view name defaults to the action name, so View(model) inside Index is shorthand for View("Index", model). They are the same call. Either way the search order is Views/{ControllerName}/{ViewName}.cshtml first, then Views/Shared/{ViewName}.cshtml. A GET of / in the example below returns status 200 and the rendered HTML of Views/Home/Index.cshtml.

What trips people up

The view is found by file name while the request runs, not when the project is built. A missing or misspelled file throws InvalidOperationException and lists every path it searched.

Example

Example · csharp
// Program.cs of a web project (<Project Sdk="Microsoft.NET.Sdk.Web">).
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// Controllers plus the Razor view engine.
builder.Services.AddControllersWithViews();

var app = builder.Build();

// A URL with no segments is treated as /Home/Index.
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

// A controller is a public class whose name ends in Controller.
public class HomeController : Controller
{
    public IActionResult Index()
    {
        // The model: one object holding everything the view needs.
        var greeting = new Greeting("Ada", 3);

        // Shorthand. The view name comes from the action name, Index.
        return View(greeting);

        // Longhand, identical: return View("Index", greeting);
    }
}

// A record is a short way to declare a class that only holds data.
public record Greeting(string Name, int Visits);

When to use it

  • Add a server-rendered admin screen to a service that already serves JSON.
  • Render a product page as HTML so a search engine can read it without running JavaScript.
  • Split one long request handler into a controller that gathers data and a view that formats it.

More examples

The view the action renders

The @model line declares the type of the object the action passed in, and @Model is that object. Declaring the type is what makes the view fail to compile, rather than fail in a browser, when a property is renamed.

Example · cshtml
@* Views/Home/Index.cshtml *@
@model Greeting

<h1>Hello, @Model.Name</h1>
<p>Visits so far: @Model.Visits</p>

@* Rendered for the Greeting("Ada", 3) above:
<h1>Hello, Ada</h1>
<p>Visits so far: 3</p>
*@

Naming the view, and the two spellings of ViewData

Reach for an explicit view name when two actions share one view file. ViewData is useful for small extras such as a page title, but it is checked only at run time, so anything the page really needs belongs on the model instead.

Example · csharp
using Microsoft.AspNetCore.Mvc;

public class ReportsController : Controller
{
    public IActionResult Daily()
    {
        // One dictionary, two spellings. ViewBag.Title is shorthand
        // for ViewData["Title"], and both read and write the same entry.
        ViewData["Title"] = "Daily report";
        ViewBag.Rows = 12;

        // An explicit name. Views/Reports/_Report.cshtml is tried first,
        // then Views/Shared/_Report.cshtml.
        return View("_Report");
    }
}

An action that returns something other than HTML

The return type IActionResult lets one action choose between rendering HTML, redirecting the browser and returning a bare status code. Redirecting after a successful form post is the normal pattern, so a page refresh does not post the form twice.

Example · csharp
using Microsoft.AspNetCore.Mvc;

public class CartController : Controller
{
    public IActionResult Item(int id)
    {
        if (id <= 0)
        {
            return NotFound();                       // 404, no view
        }

        return RedirectToAction(nameof(Index));      // 302 to /Cart/Index
    }

    // Expression-bodied member: the same as { return View(); }
    public IActionResult Index() => View();
}

Discussion

  • Be the first to comment on this lesson.