.NET ProgrammingUnit 810 min read

ASP.NET Core: MVC, Razor Pages, Middleware & Deployment

Unit 8 of .NET Programming covers ASP.NET Core’s architecture, Model-View-Controller (MVC) pattern, Razor Pages, middleware pipeline, dependency injection, and deployment strategies (IIS, Docker, Azure). It explains how to build scalable web apps with routing, filters, and real-world hosting.

ASP.NET Core Architecture Overview

ASP.NET Core is a cross-platform, high-performance framework for building modern web apps. It is modular, lightweight, and supports:

  • Cross-platform: Runs on Windows, Linux, and macOS.
  • Modular: Only include what you need (e.g., MVC, Razor Pages, Web API).
  • Dependency Injection (DI): Built-in support for DI via IServiceProvider.
  • Middleware Pipeline: Requests pass through a chain of middleware components (e.g., authentication, logging).

Key Components

classDiagram
  class ASPNETCore {
      +Middleware Pipeline
      +Dependency Injection
      +Routing
      +Configuration
      +Logging
      +Razor Pages
      +Controllers
  }
  class Middleware {
      <<interface>>
      +Invoke(HttpContext context)
      +InvokeAsync(HttpContext context)
  }
  class Controller {
      <<abstract>>
      +ActionResult Index()
      +[static] OnActionExecuting()
  }
  class RazorPage {
      <<class>>
      +OnGet()
      +OnPost()
  }
  class Model {
      <<class>>
      +Properties
      +Validation
  }
  class View {
      <<class>>
      +Render(Model model)
      +Partial Views
  }
  ASPNETCore --> Middleware : "Uses Pipeline"
  ASPNETCore --> Controller : "Contains"
  ASPNETCore --> RazorPage : "Contains"
  Controller --> Model : "Uses"
  Controller --> View : "Returns"
  RazorPage --> Model : "Uses"
  RazorPage --> View : "Renders"
  Middleware --> Middleware : "Chains"
Updated class diagram showing MVC + Razor Pages relationship with ASP.NET Core

Model-View-Controller (MVC) Pattern

MVC separates an application into three interconnected components:

  1. Model: Manages data and business logic (e.g., DbContext, entities).
  2. View: Displays UI (Razor views, .cshtml files).
  3. Controller: Handles HTTP requests, processes data, and returns views.

Worked Example: Simple MVC Flow

Scenario: A blog app where users view posts.

// Model (BlogPost.cs)
public class BlogPost {
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}

```figure
{"type":"network","nodes":["User","Controller","Model","View","Database"],"edges":[["User","Controller",{"label":"HTTP Request"}],["Controller","Model",{"label":"Fetch Data"}],["Model","Database",{"label":"Query"}],["Database","Model",{"label":"Result"}],["Controller","View",{"label":"Render"}],["View","User",{"label":"Response"}]],"caption":"Complete MVC request-response cycle with database interaction"}

// Controller (PostsController.cs) public class PostsController : Controller { public IActionResult Index() { var posts = new List<BlogPost> { new BlogPost { Id = 1, Title = "First Post", Content = "Hello!" } }; return View(posts); // Renders Index.cshtml } }

// View (Index.cshtml) @model List<BlogPost>

<h1>Blog Posts</h1> <ul> @foreach (var post in Model) { <li>@post.Title</li> } </ul>

Trace:

Step Action State After Step
1 User visits /Posts/Index Request reaches PostsController.Index()
2 Controller fetches BlogPost posts list populated
3 return View(posts) Razor engine renders Index.cshtml
4 View displays HTML User sees rendered page

Razor Pages

Razor Pages is a simpler alternative to MVC for page-focused apps. Key features:

  • Page-based: Each .cshtml file is a self-contained page with a code-behind file (.cs).
  • No controllers: Logic is embedded in the page.
  • Tag Helpers: HTML-like syntax for server-side logic (e.g., @Html.TextBox).

Example: Razor Page for User Login

File: Login.cshtml

@page
@model LoginModel
<h1>Login</h1>
<form method="post">
    <input asp-for="Username" />
    <input asp-for="Password" type="password" />
    <button type="submit">Login</button>
</form>

Code-behind: Login.cshtml.cs

public class LoginModel : PageModel {
    [BindProperty]
    public string Username { get; set; }
    public string Password { get; set; }

    public void OnGet() { }

    public IActionResult OnPost() {
        if (Username == "admin" && Password == "123") {
            return RedirectToPage("/Dashboard");
        }
        return Page(); // Re-renders with errors
    }
}

Trace:

Step Action State After Step
1 User submits form OnPost() triggered
2 Validates credentials Username and Password bound
3 Redirects to /Dashboard User navigates to secure page

Middleware Pipeline

Middleware components process HTTP requests/responses in a pipeline. Common middleware:

  • Authentication: JwtBearer, CookieAuthentication.
  • Logging: ILogger.
  • Error Handling: ExceptionHandler.
  • Static Files: StaticFilesMiddleware.

Example: Custom Middleware for Logging

public class LoggingMiddleware {
    private readonly RequestDelegate _next;

    public LoggingMiddleware(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context) {
        Console.WriteLine($"Request: {context.Request.Path}");
        await _next(context); // Pass to next middleware
        Console.WriteLine($"Response: {context.Response.StatusCode}");
    }
}

Register in Program.cs:

app.UseMiddleware<LoggingMiddleware>();

Trace:

flowchart TD
    A["Request"] --> B["LoggingMiddleware: Logs path"]
    B --> C["Next Middleware"]
    C --> D["Response"]
    D --> B["LoggingMiddleware: Logs status"]

Dependency Injection (DI)

ASP.NET Core’s built-in DI simplifies managing services (e.g., DbContext, ILogger). Register services in Program.cs:

builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddTransient<IEmailService, EmailService>();

Example: Injecting IUserRepository into a controller.

public class UsersController : Controller {
    private readonly IUserRepository _repo;

    public UsersController(IUserRepository repo) {
        _repo = repo;
    }

    public IActionResult Index() {
        var users = _repo.GetAll();
        return View(users);
    }
}

Routing

Routing maps URLs to controllers/actions. Default conventions:

  • /Controller/Action/Id → Controller.Action(int id).
  • Attribute routing: [Route("api/[controller]")].

Example: Custom route for a blog.

[Route("api/posts/{id}")]
public class PostsController : Controller {
    [HttpGet]
    public IActionResult Get(int id) {
        return Ok(new { Id = id, Title = "Sample Post" });
    }
}

Trace:

URL Action Output
/api/posts/1 Get(1) { "Id": 1, "Title": "Sample" }

In the Real World

  1. eSewa (Nepal):

    • Uses ASP.NET Core for its backend API to handle online bill payments (electricity, telecom).
    • Middleware: Validates user sessions and logs transactions.
    • Razor Pages: Admin dashboard for viewing payment records.
  2. Khalti (Nepal):

    • Employs MVC pattern to separate payment processing (Model), UI (View), and request handling (Controller).
    • Dependency Injection: Manages services like IPaymentGateway for seamless integrations.
  3. YouTube (Global):

    • Built on ASP.NET Core (via .NET’s influence on modern web stacks).
    • Middleware Pipeline: Handles authentication (OAuth), rate limiting, and caching.
    • Routing: Maps /watch?v=abc123 to video retrieval logic.

Deployment Strategies

Deploy ASP.NET Core apps to:

  1. IIS (Windows): Use web.config and publish as a folder.
  2. Docker: Containerize apps for consistency across environments.
  3. Azure App Service: Fully managed PaaS with auto-scaling.
Azure App ServiceDocker ContainerIIS ServerLinux ServerTOP
Common ASP.NET Core deployment environments (bottom to top)

Example: Dockerfile for ASP.NET Core.

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["ASP.NETCoreApp.csproj", "."]
RUN dotnet restore "ASP.NETCoreApp.csproj"
COPY . .
RUN dotnet publish "ASP.NETCoreApp.csproj" -c Release -o /app/publish

FROM build AS publish
RUN dotnet publish "ASP.NETCoreApp.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ASP.NETCoreApp.dll"]

Trace:

Step Action Output
1 docker build -t myapp . Container image built
2 docker run -p 8080:80 myapp App runs on http://localhost:8080

Exam Tip

  1. MVC vs. Razor Pages:

    • MVC is better for complex apps with many controllers.
    • Razor Pages is simpler for page-focused apps (e.g., admin panels).
    • Exam Pitfall: Confusing ViewBag/ViewData (dynamic) vs. strongly typed models.
  2. Middleware Order:

    • Middleware runs top-to-bottom in Program.cs. Example:
      app.UseHttpsRedirection(); // Must be first for HTTPS
      app.UseStaticFiles();      // Serve static files
      app.UseRouting();          // Enable endpoint routing
      
    • Exam Question: "Why does UseRouting() fail if placed before UseEndpoints()?"
  3. DI Scopes:

    • Transient: New instance per request.
    • Scoped: One instance per HTTP request.
    • Singleton: One instance for the app lifetime.
    • Exam Question: "When would you use Scoped for DbContext?"
  4. Routing:

    • Memorize attribute routing syntax: [Route("api/[controller]")].
    • Exam Question: "How would you map /products/{category} to a controller?"
  5. Deployment:

    • Know the difference between self-contained (includes .NET runtime) and framework-dependent deployments.
    • Exam Question: "Why might you choose Docker over IIS for deployment?"

Based on the TU BITM syllabus for .NET Programming (IT275), unit 8.

Discussion

Loading…