ASP.NET CORE MVC · .NET 10
ASP.NET Core MVC: Complete Developer Guide
A practical reference to the modern MVC framework: controllers, routing, Razor views, model binding, validation, forms, filters, security, dependency injection, testing, and deployment.
ASP.NET Core MVC is a framework for building server-rendered web applications using the Model–View–Controller (MVC) architectural pattern. It is part of ASP.NET Core and runs on modern .NET, including .NET 10.
This guide explains the concepts and development workflow needed to build maintainable MVC applications. It uses the modern ASP.NET Core hosting model, Razor views, built-in dependency injection, and the current Microsoft.AspNetCore.Mvc APIs.
net10.0. It is separate from classic ASP.NET MVC 5 on .NET Framework. Do not mix in System.Web.Mvc, Global.asax, App_Start, or classic MVC 5 configuration examples. This guide focuses on MVC views and server-rendered web applications; ASP.NET Core Web API will have its own dedicated guide.1. What Is ASP.NET Core MVC?
ASP.NET Core MVC is a web application framework that combines the MVC pattern with ASP.NET Core's request pipeline, routing, dependency injection, configuration, logging, and hosting capabilities.
It is commonly used to build:
- Business applications and administration portals.
- Content management systems.
- Customer and employee portals.
- Server-rendered websites with dynamic pages.
- Applications that use Razor views and HTML forms.
MVC is not a programming language. C# is commonly used to write controllers, models, and application services, while Razor (.cshtml) is used to create dynamic HTML views.
2. MVC Architecture and Request Flow
MVC separates an application into three main components:
| Component | Responsibility | Example |
|---|---|---|
| Model | Represents application data and rules used by the application. | Product, Customer, Order, or a ViewModel. |
| View | Renders the user interface, usually HTML, using Razor. | Views/Products/Index.cshtml. |
| Controller | Receives a request, coordinates application work, and selects a response. | ProductsController. |
How an MVC request works
- A browser sends an HTTP request to the application.
- ASP.NET Core middleware processes the request.
- Endpoint routing selects the matching controller action.
- Model binding obtains action parameter values from the request.
- Validation runs and populates ModelState.
- The controller calls application services to perform the required work.
- The action returns a result, such as a view, redirect, or status code.
- If a view is returned, Razor renders HTML and the response is sent to the browser.
3. Creating an ASP.NET Core MVC Project in .NET 10
Install the .NET 10 SDK from the official .NET download page. Then create an MVC project using the CLI.
dotnet new mvc -n StoreMvc
cd StoreMvc
dotnet run
The command creates an ASP.NET Core MVC application. The terminal displays the local addresses where the application is listening. Open the HTTPS address in a browser and trust the local development certificate if prompted.
Create the project in Visual Studio
- Select Create a new project.
- Choose ASP.NET Core Web App (Model-View-Controller).
- Enter the project name and location.
- Select .NET 10.0 as the framework.
- Choose the required authentication option, or select None for a learning project.
- Create and run the project.
net10.0 in its project file.4. ASP.NET Core MVC Project Structure
A typical MVC project created from the template has a structure similar to this:
StoreMvc/
│
├── Controllers/
│ └── HomeController.cs
│
├── Models/
│ └── ErrorViewModel.cs
│
├── Views/
│ ├── Home/
│ │ ├── Index.cshtml
│ │ └── Privacy.cshtml
│ ├── Shared/
│ │ ├── _Layout.cshtml
│ │ ├── _ValidationScriptsPartial.cshtml
│ │ └── Error.cshtml
│ ├── _ViewImports.cshtml
│ └── _ViewStart.cshtml
│
├── wwwroot/
│ ├── css/
│ ├── js/
│ └── lib/
│
├── Properties/
│ └── launchSettings.json
│
├── appsettings.json
├── appsettings.Development.json
├── Program.cs
└── StoreMvc.csproj
| Folder/file | Purpose |
|---|---|
Controllers | Contains MVC controller classes and action methods. |
Models | Contains model classes and often ViewModels. |
Views | Contains Razor view files organized by controller. |
Views/Shared | Contains layouts, shared views, and reusable view files. |
wwwroot | Web root for static files such as CSS, JavaScript, images, and client libraries. |
Program.cs | Configures services and the HTTP request pipeline. |
appsettings.json | Contains application configuration settings. |
launchSettings.json | Contains local development launch profiles; it is not normally deployed as production configuration. |
.csproj | Defines the project target framework, SDK, package references, and build properties. |
5. Program.cs and Application Startup
Modern ASP.NET Core applications use the minimal hosting model. The Program.cs file registers services and configures middleware and endpoints. This is different from classic MVC 5 startup through Global.asax and App_Start.
var builder = WebApplication.CreateBuilder(args);
// Register MVC services.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Service registration vs middleware
- Service registration:
builder.Servicesregisters objects and framework services in the dependency injection container. - Middleware configuration:
app.Use...adds components to the HTTP request pipeline. - Endpoint mapping:
app.MapControllerRoutemaps URL patterns to MVC controller actions.
Middleware order matters. For example, routing must be configured before endpoint execution, and authentication should run before authorization when authentication is enabled.
6. Controllers and Action Methods
A controller is a class that typically derives from Controller. Public methods that are eligible for action selection handle incoming requests. Methods marked with [NonAction] are not treated as actions.
using Microsoft.AspNetCore.Mvc;
public class ProductsController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Details(int id)
{
return Content($"Product ID: {id}");
}
[HttpGet]
public IActionResult Create()
{
return View();
}
}
By convention, ProductsController is associated with the Views/Products folder. The Index action normally looks for Views/Products/Index.cshtml.
Common action return types
| Return type/result | Purpose |
|---|---|
IActionResult | Flexible result type for views, redirects, status codes, and other MVC results. |
ViewResult | Renders a Razor view. |
RedirectToActionResult | Redirects to another controller action. |
RedirectResult | Redirects to a URL. |
ContentResult | Returns text content. |
JsonResult | Returns JSON data. |
NotFoundResult | Returns HTTP 404. |
BadRequestResult | Returns HTTP 400. |
FileResult | Returns file content. |
EmptyResult | Produces an empty result. |
Common action attributes
[HttpGet],[HttpPost],[HttpPut],[HttpDelete]– constrain the action to an HTTP method.[ActionName("...")]– specifies an action name for routing.[NonAction]– prevents a public method from being selected as an action.[Authorize]– requires authorization according to the configured authentication scheme and policy.[AllowAnonymous]– permits anonymous access to an action or controller.
7. Routing in ASP.NET Core MVC
Routing maps an incoming URL to an endpoint, such as a controller action. ASP.NET Core MVC supports conventional routing and attribute routing.
Conventional routing
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
For this route, /Products/Details/15 maps to the Details action in ProductsController, with id set to 15.
Attribute routing
public class ProductsController : Controller
{
[HttpGet("products")]
public IActionResult Index()
{
return View();
}
[HttpGet("products/{id:int}")]
public IActionResult Details(int id)
{
return View();
}
}
Route constraints
Constraints restrict route values to expected formats. Examples include {id:int}, {slug:alpha}, and {date:datetime}. Constraints help route matching; they do not replace validation of business input.
URL generation
Use routing helpers instead of manually concatenating URLs:
@Html.ActionLink("View products", "Index", "Products")
View product
URL generation helpers reduce errors when route templates change.
8. Razor Views and Strongly Typed Views
Razor is the view engine used by ASP.NET Core MVC. Razor files have the .cshtml extension and combine HTML markup with C# expressions and control structures.
Simple Razor view
@{
ViewData["Title"] = "Home";
}
Welcome
Today is @DateTime.Now.ToShortDateString()
Strongly typed views
A strongly typed view declares its model using the @model directive. This provides compile-time type checking and IntelliSense for model properties.
// Models/ProductViewModel.cs
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
// Controller
public IActionResult Details()
{
var product = new ProductViewModel
{
Id = 10,
Name = "Keyboard",
Price = 1200
};
return View(product);
}
// Views/Products/Details.cshtml
@model ProductViewModel
@Model.Name
Product ID: @Model.Id
Price: @Model.Price.ToString("C")
Razor automatically HTML-encodes ordinary expressions such as @Model.Name. Avoid using Html.Raw with untrusted input because it bypasses output encoding and can introduce cross-site scripting vulnerabilities.
9. Layouts, Sections and Partial Views
Layout pages
A layout provides the common page structure shared by multiple views, such as navigation, header, footer, CSS references, and script references. The default layout is commonly stored at Views/Shared/_Layout.cshtml.
<!DOCTYPE html>
<html>
<head>
<title>@ViewData["Title"] - Store</title>
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>Store Application</header>
<main role="main">
@RenderBody()
</main>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
_ViewStart.cshtml
_ViewStart.cshtml runs before views in its scope and is commonly used to assign the layout:
@{
Layout = "_Layout";
}
_ViewImports.cshtml
_ViewImports.cshtml makes namespaces and Tag Helpers available to Razor views:
@using StoreMvc
@using StoreMvc.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Partial views
A partial view is a reusable Razor fragment. It is useful for repeated markup that does not need its own independent logic or lifecycle.
// Render a partial
<partial name="_ProductSummary" model="Model" />
// Or
@await Html.PartialAsync("_ProductSummary", Model)
Use partials for presentation reuse. If a reusable UI section needs to execute its own logic or load data through dependency injection, consider a View Component.
10. View Components
A View Component is a reusable UI component that can execute logic and return a view. It is useful for components such as shopping-cart summaries, navigation menus, or dashboard widgets.
using Microsoft.AspNetCore.Mvc;
public class CartSummaryViewComponent : ViewComponent
{
public IViewComponentResult Invoke(int itemCount)
{
return View(itemCount);
}
}
Its default view location is Views/Shared/Components/CartSummary/Default.cshtml.
@model int
<div class="cart-summary">
Items in cart: @Model
</div>
Invoke it from a Razor view:
@await Component.InvokeAsync(
"CartSummary",
new { itemCount = 3 })
View Components are not controller actions and are not directly addressable as HTTP endpoints.
11. Models, ViewModels and DTOs
A model represents data and rules used by an application. In MVC applications, it is important to distinguish the database entity from the data required by a particular screen.
- Entity/domain model: represents application or persisted business data.
- ViewModel: contains the data a particular view needs to display or submit.
- DTO: defines a data contract used to transfer data between application boundaries or services.
For example, a registration form might need a password confirmation field that should not be stored in the user entity. A dedicated ViewModel is appropriate.
using System.ComponentModel.DataAnnotations;
public class RegisterViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; } = "";
[Required]
[DataType(DataType.Password)]
public string Password { get; set; } = "";
[Required]
[DataType(DataType.Password)]
[Compare(nameof(Password))]
public string ConfirmPassword { get; set; } = "";
}
Do not bind an unrestricted database entity directly from a form when the user should only be able to update a subset of its properties. Use a ViewModel and explicitly map allowed values.
12. Model Binding
Model binding maps incoming request data to action parameters or model properties. MVC can bind values from route data, query strings, form fields, and other supported request sources.
public IActionResult Search(string term, int page = 1)
{
// /Products/Search?term=keyboard&page=2
return View();
}
Binding source attributes
| Attribute | Source |
|---|---|
[FromRoute] | Route values. |
[FromQuery] | Query-string values. |
[FromForm] | Form fields. |
[FromHeader] | HTTP headers. |
[FromBody] | Request body, commonly for input formatted as JSON. |
These attributes are useful when the source needs to be explicit. Traditional MVC form actions commonly bind form values to a model without requiring an attribute on every parameter.
[HttpGet]
public IActionResult Details(
[FromRoute] int id,
[FromQuery] bool includeReviews)
{
return View();
}
[FromBody] is commonly used for JSON request bodies and is more prominent in API-style actions. For standard browser form submissions, use form binding and a suitable ViewModel. A request body should not be read independently by multiple body-bound parameters.13. Model Validation
Model validation checks submitted values against validation rules. Data annotations are commonly used to define validation requirements.
using System.ComponentModel.DataAnnotations;
public class ProductCreateViewModel
{
[Required]
[StringLength(100, MinimumLength = 2)]
public string Name { get; set; } = "";
[Range(0.01, 100000)]
public decimal Price { get; set; }
[Required]
public int CategoryId { get; set; }
}
Check ModelState.IsValid before processing a submitted form:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ProductCreateViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Call application service to save the product.
return RedirectToAction(nameof(Index));
}
Common validation attributes
[Required]– value is required.[StringLength]– limits string length.[MinLength]and[MaxLength]– set length limits.[Range]– restricts numeric or date values to a range.[RegularExpression]– validates against a regular expression.[EmailAddress]– validates an email-like value.[Compare]– compares two properties.
Custom validation can be implemented with a custom ValidationAttribute or IValidatableObject. Server-side validation is essential even when client-side validation is enabled.
14. Forms, Tag Helpers and HTML Helpers
Form Tag Helper
Tag Helpers allow server-side code to participate in generating HTML elements. The Form Tag Helper can generate the action URL and antiforgery token for a form.
<form asp-controller="Products"
asp-action="Create"
method="post">
<label asp-for="Name"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name"
class="text-danger"></span>
<label asp-for="Price"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price"
class="text-danger"></span>
<button type="submit">Save</button>
</form>
HTML Helpers
HTML Helpers are methods used in Razor views to generate HTML. Examples include:
@Html.TextBoxFor(m => m.Name)
@Html.LabelFor(m => m.Name)
@Html.ValidationMessageFor(m => m.Name)
@Html.DropDownListFor(m => m.CategoryId, Model.Categories)
Tag Helpers vs HTML Helpers
| Tag Helpers | HTML Helpers |
|---|---|
| Use HTML-like syntax and attributes. | Use C# method calls inside Razor. |
| Often easier for designers to read alongside HTML. | Familiar to developers coming from earlier MVC versions. |
Examples: asp-for, asp-action, asp-route-id. | Examples: TextBoxFor, LabelFor, ActionLink. |
Client-side validation commonly uses the validation scripts included by the template. These improve user experience but do not replace server-side validation.
15. ViewData, ViewBag, TempData and Session
| Mechanism | Type / lifetime | Typical use |
|---|---|---|
ViewData | Dictionary-based data for the current request. | Small values passed from controller to view. |
ViewBag | Dynamic wrapper over ViewData for the current request. | Convenient access to small view values. |
TempData | Data retained for a subsequent request, commonly across a redirect. | One-time success or error messages. |
Session | Per-user session state stored using a configured session provider. | Small pieces of temporary user-specific state. |
| Strongly typed ViewModel | Explicit model passed to a view. | Preferred for structured screen data. |
ViewData and ViewBag
ViewData["Title"] = "Products";
ViewBag.Message = "Product list";
TempData
TempData["Success"] = "Product saved successfully.";
return RedirectToAction(nameof(Index));
TempData is useful for Post/Redirect/Get workflows. Its default provider uses cookies; a session-backed provider can also be configured.
Session
Session requires registering a distributed cache and session services, then adding session middleware in the request pipeline.
// Service registration
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Middleware: after routing and before endpoint execution
app.UseSession();
Use session sparingly. It is not a replacement for a database, durable storage, or a distributed application data model. In multi-server deployments, choose an appropriate shared distributed cache.
16. Filters in ASP.NET Core MVC
Filters run code at specific stages around MVC action execution. They are useful for cross-cutting concerns such as authorization, logging, exception handling, and response modification.
| Filter type | Purpose |
|---|---|
| Authorization filters | Run early to determine whether the user may access a resource. |
| Resource filters | Run before and after most of the MVC pipeline, useful for caching or short-circuiting. |
| Action filters | Run before and after an action method. |
| Exception filters | Handle exceptions from controller creation, model binding, action filters, and action execution in their scope. |
| Result filters | Run before and after execution of an action result. |
| Always-run result filters | Run around result execution even for certain short-circuit paths. |
Built-in filter attributes
[Authorize]– requires authorization.[AllowAnonymous]– permits anonymous access.[ValidateAntiForgeryToken]– validates an antiforgery token.[AutoValidateAntiforgeryToken]– validates antiforgery tokens for unsafe methods, according to its configuration.[ResponseCache]– adds response caching metadata.[ServiceFilter]and[TypeFilter]– support filters created through dependency injection.
Custom action filter example
using Microsoft.AspNetCore.Mvc.Filters;
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(
ActionExecutingContext context)
{
// Add request/action logging here.
base.OnActionExecuting(context);
}
}
Filters are specific to MVC execution. Middleware is broader and can run for requests that do not reach MVC. Choose the mechanism based on the scope of the concern.
17. Dependency Injection and Application Services
ASP.NET Core includes a built-in dependency injection container. Controllers receive required services through constructor injection.
public interface IProductService
{
Task<IReadOnlyList<ProductViewModel>> GetAllAsync();
}
public class ProductsController : Controller
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
public async Task<IActionResult> Index()
{
var products = await _productService.GetAllAsync();
return View(products);
}
}
Register the service in Program.cs:
builder.Services.AddScoped<IProductService, ProductService>();
Service lifetimes
| Lifetime | Behavior | Typical use |
|---|---|---|
| Transient | A new instance is created each time it is requested. | Lightweight stateless services. |
| Scoped | One instance is created per request scope in a web request. | Application services and EF Core DbContext. |
| Singleton | One instance is used for the application's lifetime. | Thread-safe shared services and caches. |
18. Working with Data and Entity Framework Core
MVC does not require a particular data-access technology. An MVC application can use Entity Framework Core, Dapper, ADO.NET, an HTTP service, or another repository/data-access implementation.
In a typical .NET 10 application using EF Core, the controller depends on an application service, and that service uses a DbContext or repository abstraction.
public class ProductService : IProductService
{
private readonly ApplicationDbContext _db;
public ProductService(ApplicationDbContext db)
{
_db = db;
}
public async Task<IReadOnlyList<ProductViewModel>> GetAllAsync()
{
return await _db.Products
.Select(p => new ProductViewModel
{
Id = p.Id,
Name = p.Name,
Price = p.Price
})
.ToListAsync();
}
}
Register the DbContext with the dependency injection container using the provider appropriate for the database. EF Core itself will be covered in a separate complete guide, including relationships, migrations, tracking, LINQ queries, and performance.
19. Authentication and Authorization
Authentication identifies the user. Authorization determines what that user is permitted to do.
Authentication
ASP.NET Core supports authentication schemes such as cookies and bearer tokens. A traditional server-rendered MVC application commonly uses cookie authentication because the browser maintains an authenticated session through a protected cookie.
Authorization
Authorization can be applied to controllers or actions using attributes and policies.
[Authorize]
public class AccountController : Controller
{
public IActionResult Profile()
{
return View();
}
[Authorize(Roles = "Admin")]
public IActionResult Administration()
{
return View();
}
[AllowAnonymous]
public IActionResult Help()
{
return View();
}
}
Policy-based authorization
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CanManageProducts",
policy => policy.RequireClaim("permission", "products.manage"));
});
[Authorize(Policy = "CanManageProducts")]
public IActionResult Manage()
{
return View();
}
Policies are useful when access rules are more detailed than a simple role check. The application must still ensure that users can access only the specific records they are permitted to view or change.
20. ASP.NET Core Identity
ASP.NET Core Identity is a membership system that provides functionality for user accounts and authentication. It supports user registration, password management, roles, claims, tokens, and related account features.
Identity can be added to an MVC application using the supported project template or by configuring Identity services and its data store. The exact setup depends on whether the application uses individual accounts, an external identity provider, or another authentication design.
Common Identity features include:
- User registration and login.
- Password hashing and password reset flows.
- Email confirmation and account lockout.
- Role and claim management.
- Two-factor authentication.
- External login providers.
Identity is not mandatory for every MVC application. Organizations may use Microsoft Entra ID or another OpenID Connect provider for centralized authentication.
21. Web Security and Antiforgery
Cross-Site Request Forgery (CSRF)
CSRF is an attack in which a malicious site attempts to cause a user's browser to send an unwanted request to an application where the user is already authenticated. MVC antiforgery protection uses a token mechanism to help verify that a state-changing form submission originated from the expected application.
For a POST form, the Form Tag Helper normally generates an antiforgery token:
<form asp-action="Delete" method="post">
<input type="hidden" name="id" value="10" />
<button type="submit">Delete</button>
</form>
Validate the token in the action or apply an appropriate global antiforgery filter:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Delete(int id)
{
// Verify permission and delete the record.
return RedirectToAction(nameof(Index));
}
Other important security practices
- Use HTTPS and secure cookie settings.
- Use authorization checks on every protected operation.
- Validate input on the server.
- Use Razor's default output encoding; avoid unsafe raw HTML.
- Protect file uploads and validate file size, type, and content.
- Store secrets outside source control using an appropriate secret store.
- Use parameterized database access through supported data libraries.
- Apply a suitable Content Security Policy and security headers for the application.
22. Exception Handling and Status-Code Pages
Production applications should handle unexpected exceptions centrally. Do not expose stack traces or internal implementation details to end users.
Exception handler middleware
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
The error action can return a friendly view. Log the underlying exception through the application's logging system, and avoid displaying sensitive exception details.
Status-code pages
Status-code pages can provide a user-friendly response for status codes such as 404 when no exception is thrown.
app.UseStatusCodePagesWithReExecute("/Home/StatusCode", "?code={0}");
Exception handling and status-code pages solve different problems: one handles unhandled exceptions; the other can provide a body or re-execute a path for certain status-code responses.
23. Configuration and Environments
ASP.NET Core configuration combines values from sources such as JSON files, environment variables, command-line arguments, and other providers.
Common files include:
appsettings.json– general application configuration.appsettings.Development.json– development-specific overrides.appsettings.Production.json– optional production-specific settings.
Read a configuration value
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection");
Strongly typed options
public class MailSettings
{
public string Host { get; set; } = "";
public int Port { get; set; }
}
builder.Services.Configure<MailSettings>(
builder.Configuration.GetSection("MailSettings"));
Inject IOptions<MailSettings> into a service that needs those values. Do not commit passwords, API keys, or production connection secrets to source control.
Environment-specific behavior
ASP.NET Core commonly uses Development, Staging, and Production environments. Use app.Environment.IsDevelopment() to enable development-only behavior, such as the developer exception page. Never expose detailed developer errors publicly.
24. Static Files and Web Assets
Static files include CSS, JavaScript, images, fonts, and other assets that do not need to be generated dynamically for each request. They are commonly stored under wwwroot.
wwwroot/
├── css/
│ └── site.css
├── js/
│ └── site.js
└── images/
└── logo.png
Enable static file serving with:
app.UseStaticFiles();
Reference a static file in a Razor view using the tilde-slash path:
<link rel="stylesheet" href="~/css/site.css" />
<script src="~/js/site.js"></script>
<img src="~/images/logo.png" alt="Logo" />
Use environment-appropriate asset versioning or cache-busting so browsers do not keep outdated CSS and JavaScript after deployment.
25. Areas in ASP.NET Core MVC
Areas help organize a large MVC application into functional sections. For example, an application might have Admin, Customer, and Reports areas.
A typical area folder structure looks like:
Areas/
└── Admin/
├── Controllers/
│ └── DashboardController.cs
└── Views/
└── Dashboard/
└── Index.cshtml
Register an area route before the default route:
app.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Mark a controller as belonging to an area:
[Area("Admin")]
public class DashboardController : Controller
{
public IActionResult Index()
{
return View();
}
}
26. File Uploads
MVC actions can receive uploaded files using IFormFile. The form must use multipart/form-data.
public class UploadViewModel
{
public IFormFile? File { get; set; }
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Upload(UploadViewModel model)
{
if (model.File == null || model.File.Length == 0)
{
ModelState.AddModelError("File", "Select a file.");
return View(model);
}
// Validate size and content, generate a safe server-side name,
// and store the file in a controlled location.
return RedirectToAction(nameof(Index));
}
Never trust the uploaded filename or extension. Enforce file-size limits, validate actual content where appropriate, generate server-side filenames, and avoid storing untrusted uploads in executable web-root locations.
27. Caching and Performance
Caching can reduce repeated work and improve response times. Select the caching mechanism based on what is being cached and whether it must be shared across application instances.
| Mechanism | Purpose |
|---|---|
| Browser/client caching | Uses HTTP cache headers to let clients reuse responses. |
| Response caching | Uses HTTP caching semantics and response headers. |
| Output caching | Stores generated endpoint responses according to configured policies. |
| Memory cache | Stores application data in the process memory. |
| Distributed cache | Stores data in a shared cache such as Redis or SQL-backed cache. |
For MVC endpoint output caching, register and enable output caching:
builder.Services.AddOutputCache();
var app = builder.Build();
app.UseRouting();
app.UseOutputCache();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Apply a policy or attribute where appropriate:
[OutputCache(Duration = 60)]
public IActionResult Catalog()
{
return View();
}
Do not cache personalized or sensitive responses publicly. Review cache variation, authorization, cookies, and invalidation behavior before enabling caching in production.
Other performance practices
- Use asynchronous I/O for database and network operations.
- Avoid unnecessary database queries and large object graphs.
- Minimize large view models and payloads.
- Use pagination for large lists.
- Optimize images, CSS, and JavaScript.
- Measure before optimizing; use profiling and production telemetry.
28. Localization and Globalization
Localization adapts an application to a user's language and culture. ASP.NET Core supports request localization, culture selection, and resource-based strings.
Register localization services and configure supported cultures:
builder.Services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
var supportedCultures = new[] { "en-US", "hi-IN" };
var localizationOptions = new RequestLocalizationOptions()
.SetDefaultCulture("en-US")
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
var app = builder.Build();
app.UseRequestLocalization(localizationOptions);
Resource files can hold translated strings. Use culture-aware formatting for dates, numbers, and currency rather than hard-coding display formats for all users.
29. Logging and Diagnostics
ASP.NET Core provides logging abstractions through ILogger. Controllers and services can request a logger through dependency injection.
public class ProductsController : Controller
{
private readonly ILogger<ProductsController> _logger;
public ProductsController(
ILogger<ProductsController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation(
"Products page requested.");
return View();
}
}
Use structured logging with message templates. Avoid logging passwords, access tokens, personal data, or sensitive request bodies. In production, connect logs and metrics to an appropriate monitoring platform and define retention and access controls.
30. Unit Testing and Integration Testing
Unit testing
Unit tests verify a small unit of application logic in isolation. A controller that depends on an application service can be tested using a mock or fake implementation of that service.
[Fact]
public void Index_ReturnsView()
{
var controller = new HomeController();
var result = controller.Index();
Assert.IsType<ViewResult>(result);
}
Integration testing
Integration tests verify that application components work together, including routing, middleware, model binding, filters, and response generation. ASP.NET Core provides Microsoft.AspNetCore.Mvc.Testing and WebApplicationFactory<TEntryPoint> for hosting an application in a test process.
Test important scenarios such as successful form submission, invalid model data, authorization failures, antiforgery behavior, 404 responses, and exception handling.
31. Publishing and Deployment
Publishing prepares an application for deployment. A framework-dependent deployment requires a compatible .NET runtime on the target machine. A self-contained deployment includes the runtime required for the selected target platform.
Publish a framework-dependent application
dotnet publish -c Release
Publish a self-contained application
dotnet publish -c Release -r win-x64 --self-contained true
Choose the runtime identifier that matches the target operating system and architecture. Self-contained deployments need to be republished to receive runtime servicing updates.
Common hosting options
- IIS: common for Windows hosting, typically using the ASP.NET Core Module.
- Linux: commonly hosted behind a reverse proxy such as Nginx or Apache, or using a supported container platform.
- Containers: package the application and its runtime environment into a container image.
- Cloud platforms: deploy to a managed application hosting service or container service.
Before production deployment, configure environment variables and secrets, HTTPS, logging, health monitoring, database connectivity, backups, and a rollback strategy.
32. ASP.NET Core MVC Best Practices
- Keep controllers small and focused on HTTP request coordination.
- Move business logic into application or domain services.
- Use strongly typed ViewModels for view input and output.
- Use server-side validation for all untrusted input.
- Use antiforgery protection for cookie-authenticated form submissions.
- Use dependency injection and choose service lifetimes carefully.
- Use asynchronous methods for I/O-bound operations.
- Use Razor's default HTML encoding and avoid unsafe raw output.
- Apply authorization to every protected action and resource.
- Keep secrets out of source control.
- Use centralized exception handling and structured logging.
- Use pagination and appropriate caching policies.
- Write unit and integration tests for critical workflows.
- Keep the .NET 10 runtime and dependencies patched.
33. ASP.NET Core MVC vs ASP.NET MVC 5
| ASP.NET Core MVC (.NET 10) | ASP.NET MVC 5 (.NET Framework) |
|---|---|
Uses Microsoft.AspNetCore.Mvc. | Uses System.Web.Mvc. |
Uses modern Program.cs hosting and configuration. | Commonly uses Global.asax, App_Start, and Web.config. |
| Built-in dependency injection abstractions. | Often integrates an external DI container. |
| Uses ASP.NET Core middleware pipeline. | Uses the classic ASP.NET System.Web pipeline. |
| Cross-platform runtime support. | Primarily Windows and .NET Framework. |
| Uses modern endpoint routing and Razor infrastructure. | Uses classic MVC routing and Razor infrastructure. |
Although both use the MVC pattern and Razor views, their hosting models, namespaces, configuration, middleware, and APIs are different. Code from MVC 5 should not be copied into an ASP.NET Core MVC project without appropriate migration.
34. Official Microsoft Documentation
- ASP.NET Core MVC overview (.NET 10)
- Controllers and action methods
- Routing to controller actions
- Razor views
- Model binding
- Model validation
- What's new in ASP.NET Core 10
- .NET support policy
What’s Next?
With ASP.NET Core MVC covered, the next complete guide will be Entity Framework Core with .NET 10. It will cover DbContext, DbSet, Code First, Database First, migrations, relationships, LINQ, change tracking, loading strategies, transactions, concurrency, and performance using current EF Core APIs.