Creating an ASP.NET Core MVC Project

๐Ÿ“‹ Table of Contents โ–พ
  1. Creating the Project
  2. Program.cs for MVC
  3. Common Mistakes

Creating an ASP.NET Core MVC project takes a single command, but understanding what is generated and why each piece exists transforms it from a magic scaffold into a structure you can confidently extend. The MVC template produces a working application with a home page, error handling, and static file serving โ€” a proper starting point rather than a blank slate. Knowing the purpose of every folder and file prevents the confusion that comes from discovering unknown pieces later.

Creating the Project

// โ”€โ”€ Create a new MVC project โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
dotnet new mvc -n BlogApp.Web -o src/BlogApp.Web

// โ”€โ”€ Generated project structure โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// BlogApp.Web/
// โ”œโ”€โ”€ Controllers/
// โ”‚   โ””โ”€โ”€ HomeController.cs          โ† default controller (Index, Privacy, Error)
// โ”œโ”€โ”€ Models/
// โ”‚   โ””โ”€โ”€ ErrorViewModel.cs          โ† view model for error page
// โ”œโ”€โ”€ Views/
// โ”‚   โ”œโ”€โ”€ Home/
// โ”‚   โ”‚   โ”œโ”€โ”€ Index.cshtml           โ† home page view
// โ”‚   โ”‚   โ””โ”€โ”€ Privacy.cshtml
// โ”‚   โ”œโ”€โ”€ Shared/
// โ”‚   โ”‚   โ”œโ”€โ”€ _Layout.cshtml         โ† master layout template
// โ”‚   โ”‚   โ”œโ”€โ”€ _ValidationScriptsPartial.cshtml
// โ”‚   โ”‚   โ””โ”€โ”€ Error.cshtml
// โ”‚   โ”œโ”€โ”€ _ViewImports.cshtml        โ† global using directives for views
// โ”‚   โ””โ”€โ”€ _ViewStart.cshtml          โ† sets default layout for all views
// โ”œโ”€โ”€ wwwroot/
// โ”‚   โ”œโ”€โ”€ css/                       โ† site CSS (site.css)
// โ”‚   โ”œโ”€โ”€ js/                        โ† site JavaScript (site.js)
// โ”‚   โ”œโ”€โ”€ lib/                       โ† client-side libraries (Bootstrap, jQuery)
// โ”‚   โ””โ”€โ”€ favicon.ico
// โ”œโ”€โ”€ appsettings.json
// โ””โ”€โ”€ Program.cs
Note: The _ViewImports.cshtml file adds global directives to all views โ€” @using BlogApp.Web.Models, @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers. Tag Helpers (the asp-for, asp-action, asp-controller attributes) are registered here. Without addTagHelper, Tag Helpers do not work in any view. The underscore prefix on _ViewImports.cshtml, _ViewStart.cshtml, _Layout.cshtml, and partial views is a convention that marks these files as non-renderable directly โ€” they support other views but are not endpoints themselves.
Tip: The _ViewStart.cshtml file sets the default layout for all views: @{ Layout = "_Layout"; }. Every view that does not explicitly set Layout = null (or override with a different layout) will be wrapped in _Layout.cshtml. This is why adding the navigation bar, footer, and common scripts to _Layout.cshtml makes them appear on every page without touching individual views. Override the layout in a specific view with @{ Layout = "_AdminLayout"; } or @{ Layout = null; } for pages that do not need the standard shell.
Warning: The wwwroot folder is the only folder served as static content by UseStaticFiles(). Files outside wwwroot (including appsettings.json, source code, and view files) are never accessible directly via HTTP. Never put files in wwwroot that should not be publicly downloadable โ€” no configuration files, no connection strings, no private keys. The wwwroot folder is the web root; everything in it is public.

Program.cs for MVC

// โ”€โ”€ MVC Program.cs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
var builder = WebApplication.CreateBuilder(args);

// AddControllersWithViews โ€” registers MVC with Razor view engine
// (vs AddControllers for Web API โ€” no view engine, no Razor)
builder.Services.AddControllersWithViews();

// Register application services
builder.Services.AddScoped<IPostService, PostService>();
builder.Services.AddDbContext<AppDbContext>(opts =>
    opts.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

var app = builder.Build();

// โ”€โ”€ Middleware pipeline for MVC โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();       // serve from wwwroot โ€” BEFORE routing
app.UseRouting();
app.UseAuthorization();

// Conventional routing โ€” the MVC default route
app.MapControllerRoute(
    name:    "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Common Mistakes

Mistake 1 โ€” Using AddControllers instead of AddControllersWithViews

โŒ Wrong โ€” Razor view engine not registered; View() returns an error:

builder.Services.AddControllers();  // no Razor โ€” views cannot render!

โœ… Correct โ€” use AddControllersWithViews() for MVC applications.

Mistake 2 โ€” Not calling UseStaticFiles before UseRouting

โŒ Wrong โ€” static files (CSS, JS) go through the routing pipeline unnecessarily, adding overhead.

โœ… Correct โ€” UseStaticFiles before UseRouting; static files short-circuit before routing.

🧠 Test Yourself

A developer creates a file at appsettings.json in the project root and accidentally copies it to wwwroot/config.json. Is this a security risk?