Table of Contents
Module 3: Building Web UI with MVC, Razor Pages & Blazor
1.1 MVC Pattern Deep Dive
Models, Views, Controllers
1.2 Razor Views
Layouts, ViewStart, Partial Views, View Components, Tag Helpers
1.3 Routing
Conventional, Attribute, Endpoint Routing
1.4 Razor Pages
Page-Centric Development
1.5 Frontend Integration
Bootstrap, Tailwind CSS, jQuery Basics
1.6 Blazor Introduction
Blazor Server vs Blazor WebAssembly, Component-Based UI
1.7 Lab: Build a Multi-Page MVC + Razor Pages App with Bootstrap-Styled Forms & Navigation
Real-Life Example: Online Bookstore App
Step-by-Step Code Implementation
Best Practices, Exception Handling, Pros/Cons, Alternatives
Introduction
Welcome to Module 3 of the Ultimate ASP.NET Full Course (2025 Edition)! This module focuses on building dynamic and interactive web user interfaces (UIs) using ASP.NET Core's powerful tools: MVC, Razor Pages, and Blazor. We'll start with a deep dive into the MVC pattern, explore Razor syntax for creating reusable views, cover routing for navigation, and introduce Razor Pages for simpler page-based development. You'll also learn to integrate popular frontend frameworks like Bootstrap and Tailwind CSS, along with jQuery for basic interactivity. Finally, we'll introduce Blazor for component-based UIs, comparing Server and WebAssembly modes.
Using real-life examples, such as an online bookstore, we'll make concepts relatable—think managing book catalogs, user forms, and interactive components. This guide is step-by-step, code-oriented, and data-focused, with best practices, exception handling, pros/cons, and alternatives throughout. The lab will have you building a multi-page app combining MVC and Razor Pages with Bootstrap styling. By the end, you'll be ready to create engaging web UIs. Let's get started!
1.1 MVC Pattern Deep Dive
Models, Views, Controllers
The Model-View-Controller (MVC) pattern is a foundational architectural design in ASP.NET Core for separating concerns, making apps more maintainable, testable, and scalable.
Models: Represent data and business logic. They define the structure of data (e.g., classes with properties) and can include validation rules. Models interact with databases via Entity Framework Core (covered in Module 3 of the full course, but we'll reference it here).
Example: A Book model for an online bookstore.
using System.ComponentModel.DataAnnotations; public class Book { [Key] public int Id { get; set; } [Required(ErrorMessage = "Title is required")] [StringLength(100, MinimumLength = 3)] public string Title { get; set; } = string.Empty; [Required] public string Author { get; set; } = string.Empty; [Range(0.01, 1000.00)] public decimal Price { get; set; } public DateTime PublishedDate { get; set; } }
Views: Handle the presentation layer, rendering HTML with data from models. Views use Razor syntax to embed C# code in HTML.
Example: A view displaying a list of books.
@model IEnumerable<Book> <h2>Book Catalog</h2> <table class="table"> <thead> <tr> <th>Title</th> <th>Author</th> <th>Price</th> </tr> </thead> <tbody> @foreach (var book in Model) { <tr> <td>@book.Title</td> <td>@book.Author</td> <td>@book.Price.ToString("C")</td> </tr> } </tbody> </table>
Controllers: Act as intermediaries, handling user input, interacting with models, and selecting views. They process HTTP requests and return responses.
Example: A BooksController for CRUD operations.
using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; public class BooksController : Controller { private readonly List<Book> _books = new List<Book>(); // Simulated data; use DbContext in real apps [HttpGet] public IActionResult Index() { return View(_books); } [HttpPost] [ValidateAntiForgeryToken] public IActionResult Create(Book book) { if (ModelState.IsValid) { _books.Add(book); return RedirectToAction(nameof(Index)); } return View(book); } }
Real-Life Analogy: MVC is like a restaurant. The Model is the kitchen (preparing data/dishes), the View is the plate presentation, and the Controller is the waiter (taking orders, coordinating).
Real-Life Example: In an online bookstore, the Model holds book data from a database, the Controller fetches books based on search queries, and the View displays them in a grid with prices and authors.
Best Practices:
Keep controllers thin: Delegate business logic to services or models.
Use data annotations for validation in models.
Implement async methods for I/O-bound operations (e.g., database calls).
Exception Handling:
Use global exception filters or middleware.
Example: Custom exception filter.
public class CustomExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext context) { context.Result = new ViewResult { ViewName = "Error" }; context.ExceptionHandled = true; } }
Register in Program.cs: builder.Services.AddControllersWithViews(options => options.Filters.Add<CustomExceptionFilter>());
Pros:
Separation of concerns improves maintainability.
Built-in support for testing (e.g., unit test controllers).
Flexible for complex apps.
Cons:
Boilerplate code for simple apps.
Overkill for static sites.
Alternatives:
Razor Pages: Simpler for page-focused apps (covered later).
Blazor: For interactive, component-based UIs.
This section alone provides a foundational understanding, but let's expand on each component for depth. Models can be extended with view models (DTOs) to avoid exposing domain models directly to views, enhancing security. For instance, a BookViewModel might include only display properties without sensitive data. Controllers can leverage dependency injection for services, like an IBookService for data access, promoting loose coupling. Views support strongly-typed models, reducing runtime errors through compile-time checks.
To make this more data-oriented, consider how models integrate with Entity Framework Core. In a real scenario, replace the in-memory list with:
private readonly AppDbContext _context;
public BooksController(AppDbContext context)
{
_context = context;
}
public async Task<IActionResult> Index()
{
var books = await _context.Books.ToListAsync();
return View(books);
}
This async approach handles large datasets efficiently, preventing thread blocking.
For advanced scenarios, implement paging with LINQ: books = await _context.Books.Skip((page-1)*pageSize).Take(pageSize).ToListAsync();. This is crucial for performance in high-traffic sites like Amazon's book search.
Exception handling in controllers can be granular: Use try-catch for specific actions, but prefer filters for global consistency. Pros of MVC include scalability—Netflix uses similar patterns for its UI layers. Cons: Learning curve for beginners; alternatives like Next.js (React) offer server-side rendering with less structure.
1.2 Razor Views
Layouts, ViewStart, Partial Views, View Components, Tag Helpers
Razor is a markup syntax that allows embedding C# code in HTML for dynamic content generation.
Layouts: Master pages defining common structure (e.g., header, footer).
Example: _Layout.cshtml
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>@ViewData["Title"] - Bookstore</title> <link rel="stylesheet" href="~/css/site.css" /> </head> <body> <header> <nav>Bookstore Navigation</nav> </header> @RenderBody() <footer> © 2025 Bookstore </footer> </body> </html>
ViewStart: Sets defaults for all views (e.g., layout).
Example: _ViewStart.cshtml
@{ Layout = "_Layout"; }
Partial Views: Reusable UI snippets.
Example: _BookCard.cshtml
@model Book <div class="card"> <h5>@Model.Title</h5> <p>By @Model.Author - $@Model.Price</p> </div>
Usage: @await Html.PartialAsync("_BookCard", book)
View Components: Reusable, logic-backed UI parts (like mini-controllers).
Example: LatestBooksViewComponent.cs
public class LatestBooksViewComponent : ViewComponent { public async Task<IViewComponentResult> InvokeAsync(int count) { var books = await GetLatestBooksAsync(count); // Fetch from DB return View(books); } }
Default view: Views/Shared/Components/LatestBooks/Default.cshtml
@model IEnumerable<Book> <h3>Latest Books</h3> @foreach (var book in Model) { <p>@book.Title</p> }
Usage: @await Component.InvokeAsync("LatestBooks", new { count = 5 })
Tag Helpers: Server-side HTML attributes for dynamic behavior.
Example: Built-in <form asp-action="Create"> generates anti-forgery tokens.
Custom Tag Helper:
[HtmlTargetElement("price", Attributes = "value")] public class PriceTagHelper : TagHelper { public decimal Value { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "span"; output.Content.SetContent(Value.ToString("C")); } }
Usage: <price value="19.99" /> renders $19.99.
Real-Life Analogy: Razor Views are like templates in a document processor—Layouts are page masters, Partials are reusable blocks, View Components are smart widgets.
Real-Life Example: In a bookstore app, use a Layout for consistent branding, Partial Views for book cards in search results, View Components for "Recommended Books" sidebars, and Tag Helpers for formatting prices dynamically.
Best Practices:
Use @section in layouts for scripts/CSS per view.
Avoid heavy logic in views; keep it in controllers or components.
Enable Tag Helper caching for performance.
Exception Handling:
Handle null models in views: @if (Model != null) { ... } else { <p>Error: No data</p> }
Use try-catch in View Components for async errors.
Pros:
Seamless C#/HTML integration.
Reusability reduces code duplication.
Cons:
Razor syntax can be error-prone for complex logic.
Server-side rendering may increase load times.
Alternatives:
Handlebars/Mustache: Client-side templating.
Vue.js Templates: For SPA integration.
Expanding on Razor: Layouts support nested layouts for hierarchical designs, e.g., admin vs public areas. ViewStart can be overridden per folder for modular apps. Partial Views are ideal for AJAX updates—load via jQuery without full page reloads. View Components support async invocation, perfect for data-fetching sidebars. Tag Helpers replace HTML helpers for cleaner markup, and custom ones can encapsulate complex UI logic, like a <pagination> tag for book lists.
In data-oriented scenarios, bind models to views with @model directives, using LINQ in views for simple transformations (but prefer controllers for complex queries). For advanced UIs, combine with frontend libs—more on that later.
Exception handling in views is limited; use custom error views or middleware. Pros include SEO-friendly SSR; cons: Less interactive than JS frameworks. Alternatives like JSX in React offer component-based rendering but require client-side setup.
1.3 Routing
Conventional, Attribute, Endpoint Routing
Routing maps URLs to actions or pages in ASP.NET Core.
Conventional Routing: Pattern-based, defined in Program.cs.
Example:
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
Matches /Books/Index to BooksController.Index().
Attribute Routing: Decorators on controllers/actions.
Example:
[Route("books")] public class BooksController : Controller { [HttpGet("{id:int}")] public IActionResult Details(int id) { // Fetch book by id return View(); } }
Matches /books/5 to Details(5).
Endpoint Routing: Unified system in .NET Core 3+ for MVC, Razor Pages, etc.
Example: app.MapControllers(); enables attribute routing.
Advanced: Custom constraints like [Route("{year:datetime}/{month:int:range(1,12)}")].
Real-Life Analogy: Routing is like a GPS—Conventional is preset routes, Attribute is custom signs, Endpoint is the navigation system.
Real-Life Example: In a bookstore, conventional routing for basic CRUD (/books/edit/1), attribute for SEO-friendly URLs (/genre/fiction/best-sellers).
Best Practices:
Use attribute routing for RESTful APIs.
Combine with areas for large apps (e.g., /Admin/Books).
Secure routes with [Authorize].
Exception Handling:
Handle 404s with app.UseStatusCodePagesWithReExecute("/Error/{0}");.
Custom route constraints to validate parameters.
Pros:
Flexible URL structures.
Improves SEO with clean URLs.
Cons:
Route conflicts can occur.
Overly complex patterns reduce readability.
Alternatives:
React Router: Client-side for SPAs.
Express Routes: Node.js equivalent.
Details on routing: Conventional is great for legacy migration, attribute for fine control (e.g., versioned APIs: [Route("api/v{version:apiVersion}/books")]). Endpoint routing supports middleware per endpoint, like rate limiting. In .NET 9, minimal APIs extend routing for quick endpoints: app.MapGet("/books", () => "Book List");.
For data scenarios, route parameters bind to model properties automatically. Handle exceptions with global handlers or IActionFilter. Pros: Decouples URLs from code structure; cons: Debugging route mismatches. Alternatives like Next.js pages offer file-based routing.
1.4 Razor Pages
Page-Centric Development
Razor Pages simplify MVC by combining controller and view in a single .cshtml file with a code-behind model.
Structure: Pages in Pages folder, e.g., Index.cshtml with Index.cshtml.cs.
Example: Pages/Books/Index.cshtml.cs
public class IndexModel : PageModel { public List<Book> Books { get; set; } = new List<Book>(); public async Task OnGetAsync() { // Fetch books Books = await GetBooksAsync(); } }
View: Pages/Books/Index.cshtml
@page @model IndexModel <h2>Books</h2> @foreach (var book in Model.Books) { <p>@book.Title</p> }
Handlers: Methods like OnGet, OnPost for HTTP verbs.
Example: Form submission.
[BindProperty] public Book NewBook { get; set; } = new Book(); public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } // Save book return RedirectToPage("Index"); }
Real-Life Analogy: Razor Pages are like self-contained rooms in a house—everything needed is in one place, unlike MVC's separate kitchen/living areas.
Real-Life Example: A bookstore contact form as a Razor Page: Handles GET (display form) and POST (submit) in one file.
Best Practices:
Use for content-heavy pages (e.g., blog posts).
Combine with MVC in hybrid apps.
Leverage @page "{id?}" for parameters.
Exception Handling:
Use try-catch in handlers.
Custom error page: Error.cshtml with OnGet.
Pros:
Less boilerplate than MVC.
Easier for simple apps.
Cons:
Less structured for complex logic.
No built-in areas like MVC.
Alternatives:
MVC: For controller-heavy apps.
SvelteKit Pages: JS-based page routing.
Razor Pages shine in scenarios like user profiles or dashboards, where page logic is self-contained. In .NET 9, they support AOT compilation for faster startups. Data binding with [BindProperty] simplifies forms, but validate inputs to prevent overposting. Exception handling can use page filters. Pros: Rapid development; cons: Scalability in large teams. Alternatives like PHP pages offer similar simplicity but less type safety.
1.5 Frontend Integration
Bootstrap, Tailwind CSS, jQuery Basics
Enhance ASP.NET UIs with CSS frameworks and JS libraries for responsive, interactive designs.
Bootstrap: Pre-built components for grids, forms, modals.
Setup: Add via CDN or LibMan.
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
Example: Styled form.
<form asp-action="Create" class="container"> <div class="mb-3"> <label asp-for="Title" class="form-label"></label> <input asp-for="Title" class="form-control" /> <span asp-validation-for="Title" class="text-danger"></span> </div> <button type="submit" class="btn btn-primary">Submit</button> </form>
Tailwind CSS: Utility-first CSS for custom designs.
Setup: Install via npm, configure in tailwind.config.js.
Example:
<div class="bg-blue-500 text-white p-4 rounded-lg"> Tailwind Card </div>
jQuery Basics: For DOM manipulation and AJAX.
Setup: CDN <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
Example: AJAX load books.
$(document).ready(function() { $('#loadBooks').click(function() { $.get('/api/books', function(data) { $('#bookList').html(data.map(book => `<p>${book.title}</p>`).join('')); }).fail(function() { alert('Error loading books'); }); }); });
Real-Life Analogy: Frontend integration is like decorating a house—Bootstrap is ready-made furniture, Tailwind is custom tools, jQuery is the handyman.
Real-Life Example: Bookstore app uses Bootstrap for responsive navigation, Tailwind for custom buttons, jQuery for dynamic cart updates without reloads.
Best Practices:
Use bundling/minification for production.
Client-side validation with jQuery Unobtrusive.
Accessibility: ARIA attributes in Bootstrap.
Exception Handling:
jQuery .fail() for AJAX errors.
Fallback CSS for no-JS users.
Pros:
Rapid styling and interactivity.
Responsive out-of-the-box.
Cons:
Bloat from unused classes (Tailwind).
jQuery is outdated; prefer vanilla JS.
Alternatives:
Bulma/Materialize: Other CSS frameworks.
Fetch API: Modern JS for AJAX.
Integration details: In ASP.NET, use wwwroot for static files. Bootstrap 5+ is JS-optional. Tailwind with PostCSS for purging unused styles. jQuery pairs with ASP.NET's asp-for for validation. In data apps, use jQuery DataTables for book grids. Handle exceptions with global AJAX handlers. Pros: Enhances UX; cons: Dependency on external CDNs. Alternatives like CSS-in-JS (Styled Components) for React integration.
1.6 Blazor Introduction
Blazor Server vs Blazor WebAssembly, Component-Based UI
Blazor lets you build interactive web UIs with C# instead of JS, using components.
Blazor Server: Runs on server, uses SignalR for real-time updates.
Pros: Full .NET access, smaller download.
Cons: Latency, always-connected.
Example: Hosted in ASP.NET Core app.
Blazor WebAssembly: Runs in browser, downloads .NET runtime.
Pros: Offline capable, no server roundtrips.
Cons: Larger initial load, limited to browser APIs.
Example: Static hosting.
Component-Based UI: Reusable .razor files with C# and HTML.
Example: BookCard.razor
@using Bookstore.Models <div class="card"> <h5>@Book.Title</h5> <p>@Book.Author - $@Book.Price</p> <button @onclick="OnAddToCart">Add to Cart</button> </div> @code { [Parameter] public Book Book { get; set; } = new Book(); private void OnAddToCart() { // Add logic } }
Usage: <BookCard Book="@currentBook" />
Real-Life Analogy: Blazor is like a smart TV—Server is streaming content, WebAssembly is downloaded apps, components are channels.
Real-Life Example: Bookstore dashboard with Blazor components for interactive book previews, real-time stock updates (Server mode).
Best Practices:
Use parameters for props.
Lifecycle methods (OnInitializedAsync) for data fetching.
State management with CascadingValue.
Exception Handling:
@try { ... } @catch (Exception ex) { <p>Error: @ex.Message</p> }
Global error boundary component.
Pros:
C# for full-stack devs.
Component reuse.
Cons:
Learning curve for JS devs.
Server mode scalability issues.
Alternatives:
React/Vue: JS-based components.
MAUI: For hybrid apps.
Blazor in .NET 9 supports AOT for WASM performance. Server mode uses WebSockets for updates, ideal for collaborative apps. Components support two-way binding @bind. In data scenarios, integrate with EF Core via services. Handle exceptions with ErrorBoundary. Pros: No JS interop needed for basics; cons: WASM size. Alternatives like Flutter Web for cross-platform.
1.7 Lab: Build a Multi-Page MVC + Razor Pages App with Bootstrap-Styled Forms & Navigation
Real-Life Example: Online Bookstore App
Let’s build a hybrid app combining MVC for book catalog (complex logic) and Razor Pages for user profile/forms (simple pages). Use Bootstrap for styling, including responsive navigation and forms. This simulates an online bookstore where users browse books (MVC), manage profiles (Razor Pages), with navigation linking both.
Step-by-Step Implementation
Setup Project:
Create ASP.NET Core Web App (MVC): dotnet new webapp -n BookstoreApp.
Add Bootstrap via CDN in _Layout.cshtml.
Add Models:
Create Book.cs and UserProfile.cs models with validation.
MVC Part: Book Catalog:
Add BooksController with Index and Details actions.
Views with Bootstrap tables/cards.
Razor Pages Part: User Profile:
Add Pages/Profile/Index.cshtml for viewing/editing profile.
Navigation:
Bootstrap navbar in layout linking MVC and Pages.
Forms & Validation:
Bootstrap-styled forms with ASP.NET validation.
Data Simulation:
Use in-memory data; extend to EF Core later.
Exception Handling:
Add error views and filters.
Code Example
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages(); // Enable Razor Pages
var app = builder.Build();
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.MapRazorPages();
app.Run();
Models/Book.cs
using System.ComponentModel.DataAnnotations;
public class Book
{
public int Id { get; set; }
[Required]
public string Title { get; set; } = string.Empty;
[Required]
public string Author { get; set; } = string.Empty;
public decimal Price { get; set; }
}
Controllers/BooksController.cs
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
public class BooksController : Controller
{
private static List<Book> _books = new List<Book>
{
new Book { Id = 1, Title = "ASP.NET Mastery", Author = "Grok", Price = 29.99m },
new Book { Id = 2, Title = "C# Deep Dive", Author = "xAI", Price = 39.99m }
};
public IActionResult Index()
{
return View(_books);
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Book book)
{
if (ModelState.IsValid)
{
book.Id = _books.Count + 1;
_books.Add(book);
return RedirectToAction(nameof(Index));
}
return View(book);
}
}
Views/Books/Index.cshtml
@model IEnumerable<Book>
<h2>Book Catalog</h2>
<table class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var book in Model)
{
<tr>
<td>@book.Title</td>
<td>@book.Author</td>
<td>@book.Price.ToString("C")</td>
</tr>
}
</tbody>
</table>
<a asp-action="Create" class="btn btn-success">Add New Book</a>
Views/Books/Create.cshtml
@model Book
<h2>Add Book</h2>
<form asp-action="Create">
<div class="mb-3">
<label asp-for="Title" class="form-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Author" class="form-label"></label>
<input asp-for="Author" class="form-control" />
<span asp-validation-for="Author" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Price" class="form-label"></label>
<input asp-for="Price" class="form-control" type="number" step="0.01" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Create</button>
</form>
Pages/Profile/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
public class ProfileIndexModel : PageModel
{
[BindProperty]
public UserProfile Profile { get; set; } = new UserProfile { Name = "User", Email = "user@example.com" };
public void OnGet()
{
// Load profile
}
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
// Save profile
return RedirectToPage("/Profile/Index");
}
}
public class UserProfile
{
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
Pages/Profile/Index.cshtml
@page
@model ProfileIndexModel
<h2>User Profile</h2>
<form method="post">
<div class="mb-3">
<label asp-for="Profile.Name" class="form-label"></label>
<input asp-for="Profile.Name" class="form-control" />
</div>
<div class="mb-3">
<label asp-for="Profile.Email" class="form-label"></label>
<input asp-for="Profile.Email" class="form-control" type="email" />
</div>
<button type="submit" class="btn btn-primary">Update</button>
</form>
Shared/_Layout.cshtml (Partial)
<!-- ... -->
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">Bookstore</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" asp-controller="Books" asp-action="Index">Books (MVC)</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-page="/Profile/Index">Profile (Razor Pages)</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<!-- Add Bootstrap CDN in <head> -->
How to Run
Create project: dotnet new webapp -n BookstoreApp.
Add files as above.
Run dotnet run.
Navigate to localhost:<port>/Books for MVC, /Profile for Razor Pages.
Add books via form; update profile.
Explanation
MVC: Handles book list/create with controller and views.
Razor Pages: Self-contained profile page with handlers.
Bootstrap: Styles forms/navigation for responsiveness.
Navigation: Links MVC and Pages seamlessly.
Validation: Built-in with data annotations and Tag Helpers.
Best Practices
Use anti-forgery tokens in forms.
Separate concerns: Data in models, logic in controllers/handlers.
Optimize for mobile with Bootstrap classes.
Exception Handling
Add Error.cshtml for global errors.
In controllers: try-catch for DB operations.
Pros
Hybrid approach leverages strengths of MVC and Pages.
Bootstrap ensures modern UI.
Cons
Managing two paradigms in one app.
In-memory data not persistent.
Alternatives
Full Blazor for interactivity.
React frontend with ASP.NET API.
This lab provides a complete, runnable app. Extend with EF Core for real data, jQuery for AJAX, or Blazor components for enhanced interactivity. The combination demonstrates practical UI building in ASP.NET.
Conclusion
Module 3 has covered building dynamic UIs with MVC, Razor Pages, and Blazor, using real-life bookstore examples. You've learned patterns, views, routing, integration, and built a hybrid app. These skills enable creating engaging web experiences.
No comments:
Post a Comment
Thanks for your valuable comment...........
Md. Mominul Islam