MVCBeginnersSeptember 2026 · 5 min read

Program.cs and Middleware in ASP.NET Core MVC Explained

By go2ismail · Published · .NET 10

TL;DR

Read Program.cs in three passes: register dependencies, build the application, then configure how requests reach endpoints. Middleware wraps request execution; endpoint mappings define the destinations.

The foundation: what the official documentation explains

The ASP.NET Core startup model uses WebApplicationBuilder to configure services and WebApplication to configure request processing. Middleware can perform work before and after the next component, or stop the pipeline. Ordering therefore affects which components see a request and which can handle failures from later components.

Implementation context: The examples use a .NET 10 MVC application organized into feature folders (Vertical Slice Architecture). Basic C# classes and async/await are assumed. Reference excerpts show selected parts of that application; separately labeled teaching adaptations explain alternatives. They are not complete standalone projects.

Separate startup work from request work

Beginners often read Program.cs as though every line executes again for every request. Service registration and application construction happen during startup. Middleware delegates execute as requests arrive. Endpoint registration also happens at startup, but the endpoint delegates run when matched. This difference explains why a database bootstrap error can prevent the whole site from starting while a feature error affects only one operation.

The reference application groups infrastructure registrations behind an extension method. That method is ordinary C# code: follow its definition to see database, authentication, storage, logging-related services, and other registrations. Its short name is not a framework feature.

Reference excerpt: Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add Serilog logging
builder.AddSerilogBuilder();

// Register all infrastructure services via centralized DI
builder.Services.AddInfrastructureDI(builder.Configuration);

Registration does not create a route

The app calls AddControllersWithViews() before Build(). This enables MVC services; it does not by itself expose a particular controller URL. Later, the area route enables addresses such as /Admin/Country/Index. Keep the two responsibilities separate when a service resolves correctly but the route still returns not found.

Reference excerpt: Program.cs

app.MapControllerRoute(
    name: "areas",
    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}")
    .WithStaticAssets();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}",
    defaults: new { area = "Public" })
    .WithStaticAssets();

The area route is placed before the default route in this implementation. The default route selects the Public area when the caller does not provide one. Those defaults are application navigation decisions. Another MVC app can select a different home controller without changing the hosting model.

Understand middleware order with a small example

The following teaching example is independent of the application startup file. Put it before endpoint execution in a small local app. The before message happens first, the endpoint writes the response, and the after message runs when control returns. Reading it as a nested call makes exception handling and response timing easier to understand.

Teaching example: middleware around an endpoint

app.Use(async (context, next) =>
{
    app.Logger.LogInformation("Before {Path}", context.Request.Path);
    await next(context);
    app.Logger.LogInformation("After {Status}", context.Response.StatusCode);
});

app.MapGet("/pipeline-demo", () => Results.Text("Hello from the endpoint"));

Follow the real endpoint registration chain

Near the bottom of the reference Program.cs, MapFeatureEndpoints() delegates to area-specific mapping methods. The Admin mapper calls MapCountryEndpoints(), which creates /api/country. A feature can have perfectly valid C# files yet no reachable API if this chain never includes its mapper.

The app also maps Razor Pages for its Identity UI. A mixed application can therefore have conventional MVC routes, Razor Pages, and explicit Minimal API routes. When two screens behave differently, identify which mechanism serves each one before assuming a framework inconsistency.

Reference excerpt: Areas/MapFeatureEndpoints.cs

public static void MapFeatureEndpoints(this IEndpointRouteBuilder app)
    {
        app.MapAdminEndpoints();
        app.MapMainEndpoints();
        app.MapAIEndpoints();
    }

Account for custom behavior before copying it

The reference pipeline includes a custom response buffer that redirects 404 and server-error responses to an HTML error page. That affects API troubleshooting: a handler can produce a not-found result, yet the client can ultimately receive a redirect or HTML. A general JSON API should make an intentional decision about preserving machine-readable errors instead of copying a page-oriented redirect policy automatically.

Authentication and authorization ordering also deserves explicit attention when adapting startup. Service registration is not the same as invoking middleware. Minimal hosting can add authentication middleware automatically when the relevant services exist; when taking manual control of the pipeline, make the intended order visible and verify it with the selected authentication scheme. Do not infer the entire security behavior from one isolated line.

Check your understanding

Trace one screen URL and one API URL through the startup mappings. Then explain which registration supplies their dependencies. In a scratch app, move the logging middleware after a terminal middleware and observe that it is no longer reached. For the reference application, inspect existing behavior without changing its startup just to match a generic tutorial.

Key Takeaways

  • Registration, middleware, and endpoint mapping solve different problems.
  • Follow extension methods to see what startup actually configures.
  • Custom error middleware can change the response produced by an endpoint.

FAQ

Does AddControllersWithViews publish my controller routes?

It registers MVC services. You still need appropriate endpoint mapping, such as MapControllerRoute.

Is MapFeatureEndpoints part of ASP.NET Core?

No. It is a project extension method that groups calls to individual endpoint mappers.

Should every project copy this Program.cs?

No. Understand each dependency and middleware decision, then keep only the behavior your application needs.