专栏导航
← 上一篇:依赖注入与中间件 - ASP.NET Core 核心概念
← 第一篇:编程世界初探
第23章: MVC 模式初探
- 专栏导航
-
- [一、MVC 模式简介](#一、MVC 模式简介)
-
- [1.1 什么是 MVC?](#1.1 什么是 MVC?)
- [1.2 MVC 工作流程](#1.2 MVC 工作流程)
- [1.3 为什么使用 MVC?](#1.3 为什么使用 MVC?)
- [二、创建 MVC 项目](#二、创建 MVC 项目)
-
- [2.1 使用 Visual Studio 创建](#2.1 使用 Visual Studio 创建)
- [2.2 使用命令行创建](#2.2 使用命令行创建)
- [2.3 项目结构](#2.3 项目结构)
- 三、Controller(控制器)
-
- [3.1 控制器的作用](#3.1 控制器的作用)
- [3.2 创建控制器](#3.2 创建控制器)
- [3.3 控制器约定](#3.3 控制器约定)
- [3.4 常见的返回类型](#3.4 常见的返回类型)
- 四、View(视图)
-
- [4.1 视图的作用](#4.1 视图的作用)
- [4.2 Razor 语法](#4.2 Razor 语法)
-
- [Razor 基本语法](#Razor 基本语法)
- [4.3 创建视图](#4.3 创建视图)
- [4.4 强类型视图](#4.4 强类型视图)
- [4.5 ViewData、ViewBag、TempData](#4.5 ViewData、ViewBag、TempData)
- 五、Model(模型)
-
- [5.1 模型的作用](#5.1 模型的作用)
- [5.2 数据注解(Data Annotations)](#5.2 数据注解(Data Annotations))
- [5.3 创建输入模型](#5.3 创建输入模型)
- 六、路由(Routing)
-
- [6.1 约定路由](#6.1 约定路由)
- [6.2 特性路由](#6.2 特性路由)
- 七、完整示例:图书管理
-
- [7.1 创建模型](#7.1 创建模型)
- [7.2 创建控制器](#7.2 创建控制器)
- [7.3 创建视图](#7.3 创建视图)
- 八、本周小结
前面两篇文章我们学习了 Minimal API,这是一种简洁的 API 开发方式。但对于传统的 Web 页面开发,ASP.NET Core 提供了更强大的 MVC(Model-View-Controller) 模式。这周我们将学习 MVC 的核心概念,了解 Model、View、Controller 如何协同工作。
一、MVC 模式简介
1.1 什么是 MVC?
MVC 是一种软件架构模式,将应用程序分为三个核心部分:
| 组件 | 职责 | 说明 |
|---|---|---|
| Model(模型) | 数据和业务逻辑 | 表示应用程序的数据结构和规则 |
| View(视图) | 用户界面 | 负责显示数据给用户 |
| Controller(控制器) | 控制逻辑 | 处理用户输入,协调 Model 和 View |
1.2 MVC 工作流程
用户请求
↓
┌──────────────────────────────────────┐
│ Controller(控制器) │
│ - 接收请求 │
│ - 调用 Model 处理数据 │
│ - 选择 View 显示结果 │
└──────────────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ Model(模型) │
│ - 处理业务逻辑 │
│ - 从数据库获取数据 │
│ - 返回数据给 Controller │
└──────────────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ View(视图) │
│ - 渲染 HTML 页面 │
│ - 显示 Model 提供的数据 │
│ - 返回给浏览器 │
└──────────────────────────────────────┘
↓
用户看到页面
1.3 为什么使用 MVC?
✅ 职责分离 :Model、View、Controller 各司其职
✅ 易于维护 :修改视图不会影响业务逻辑
✅ 可重用 :Model 和 Controller 可被多个 View 使用
✅ 便于测试:可以单独测试每一层
二、创建 MVC 项目
2.1 使用 Visual Studio 创建
- 创建新项目
- 搜索 "ASP.NET Core Web 应用程序"
- 选择模板:Web 应用程序(Model-View-Controller)
- 选择框架:.NET 8.0
2.2 使用命令行创建
bash
# 创建 MVC 项目
dotnet new mvc -n MyMvcApp
# 运行项目
cd MyMvcApp
dotnet run
2.3 项目结构
MyMvcApp/
├── Controllers/ # 控制器目录
│ └── HomeController.cs
├── Views/ # 视图目录
│ ├── Home/
│ │ ├── Index.cshtml
│ │ └── Privacy.cshtml
│ └── Shared/
│ ├── _Layout.cshtml # 布局文件
│ └── _ValidationScriptsPartial.cshtml
├── Models/ # 模型目录
│ └── ErrorViewModel.cs
├── wwwroot/ # 静态文件(CSS、JS、图片)
├── Program.cs # 程序入口
└── appsettings.json # 配置文件
三、Controller(控制器)
3.1 控制器的作用
控制器是应用程序的"大脑":
- 接收用户的 HTTP 请求
- 调用业务逻辑
- 选择视图返回给用户
3.2 创建控制器
csharp
// Controllers/HomeController.cs
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
// GET: /Home/Index
public IActionResult Index()
{
return View();
}
// GET: /Home/About
public IActionResult About()
{
return View();
}
// GET: /Home/Contact
public IActionResult Contact()
{
return View();
}
}
3.3 控制器约定
- 控制器类名以
Controller结尾 - 继承自
Controller或ControllerBase - 方法称为 Action(动作)
- Action 方法返回
IActionResult
3.4 常见的返回类型
csharp
public class SampleController : Controller
{
// 返回视图
public IActionResult Index()
{
return View();
}
// 返回 JSON
public IActionResult GetData()
{
var data = new { Name = "张三", Age = 25 };
return Json(data);
}
// 返回内容
public IActionResult GetText()
{
return Content("Hello World");
}
// 返回文件
public IActionResult Download()
{
byte[] fileBytes = System.IO.File.ReadAllBytes("file.pdf");
return File(fileBytes, "application/pdf", "file.pdf");
}
// 重定向
public IActionResult RedirectToHome()
{
return RedirectToAction("Index", "Home");
}
// 返回 404
public IActionResult NotFoundExample()
{
return NotFound();
}
}
四、View(视图)
4.1 视图的作用
视图负责渲染 HTML 页面,将数据展示给用户。
4.2 Razor 语法
ASP.NET Core 使用 Razor 视图引擎,它允许在 HTML 中嵌入 C# 代码。
Razor 基本语法
html
@* 这是注释 *@
@* 输出变量 *@
<p>你好, @ViewBag.Name</p>
@* 代码块 *@
@{
var message = "Hello";
var count = 10;
}
@* 条件语句 *@
@if (count > 5)
{
<p>数量大于 5</p>
}
else
{
<p>数量小于等于 5</p>
}
@* 循环 *@
<ul>
@foreach (var item in Model)
{
<li>@item</li>
}
</ul>
@* 表达式 *@
<p>当前时间: @DateTime.Now.ToString("yyyy-MM-dd")</p>
@* HTML 编码(自动) *@
<p>@Html.Raw("<strong>粗体</strong>")</p> <!-- 不编码 -->
<p><strong>普通文本</strong></p>
4.3 创建视图
视图文件放在 Views/[ControllerName]/ 目录下,文件名与 Action 方法名相同。
html
<!-- Views/Home/Index.cshtml -->
@{
ViewData["Title"] = "首页";
}
<h1>欢迎来到首页</h1>
<p>这是我的第一个 MVC 页面。</p>
4.4 强类型视图
使用 @model 指令声明视图的模型类型。
csharp
// Models/Book.cs
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public decimal Price { get; set; }
}
csharp
// Controllers/BooksController.cs
public class BooksController : Controller
{
public IActionResult Index()
{
List<Book> books = new List<Book>
{
new Book { Id = 1, Title = "C# 入门", Author = "张三", Price = 89.5m },
new Book { Id = 2, Title = "ASP.NET Core", Author = "李四", Price = 99.0m }
};
return View(books); // 传递模型给视图
}
}
html
<!-- Views/Books/Index.cshtml -->
@model List<Book>
@{
ViewData["Title"] = "图书列表";
}
<h1>图书列表</h1>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>书名</th>
<th>作者</th>
<th>价格</th>
</tr>
</thead>
<tbody>
@foreach (var book in Model)
{
<tr>
<td>@book.Id</td>
<td>@book.Title</td>
<td>@book.Author</td>
<td>@book.Price</td>
</tr>
}
</tbody>
</table>
4.5 ViewData、ViewBag、TempData
| 特性 | 类型 | 说明 |
|---|---|---|
| ViewData | 字典 | Controller 到 View 传递数据 |
| ViewBag | 动态对象 | ViewData 的动态包装器 |
| TempData | 字典 | 在多个请求之间传递数据(如重定向) |
csharp
// Controller
public IActionResult Index()
{
ViewData["Title"] = "首页";
ViewData["Message"] = "Hello World";
ViewBag.Name = "张三";
ViewBag.Age = 25;
TempData["Notice"] = "操作成功";
return View();
}
html
<!-- View -->
@ViewData["Title"]
@ViewData["Message"]
@ViewBag.Name
@ViewBag.Age
@TempData["Notice"]
五、Model(模型)
5.1 模型的作用
模型代表应用程序的数据和业务逻辑:
- 数据模型:表示数据库表或 API 返回的数据
- 业务逻辑:验证规则、计算逻辑等
5.2 数据注解(Data Annotations)
使用数据注解为模型添加验证规则。
csharp
using System.ComponentModel.DataAnnotations;
public class Book
{
public int Id { get; set; }
[Required(ErrorMessage = "书名不能为空")]
[StringLength(100, ErrorMessage = "书名长度不能超过100个字符")]
public string Title { get; set; }
[Required(ErrorMessage = "作者不能为空")]
public string Author { get; set; }
[Range(0.01, 9999.99, ErrorMessage = "价格必须在 0.01 到 9999.99 之间")]
public decimal Price { get; set; }
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; }
}
5.3 创建输入模型
用于接收用户提交的表单数据。
csharp
// Models/CreateBookViewModel.cs
public class CreateBookViewModel
{
[Required]
[StringLength(100)]
public string Title { get; set; }
[Required]
public string Author { get; set; }
[Range(0.01, 9999.99)]
public decimal Price { get; set; }
}
六、路由(Routing)
6.1 约定路由
在 Program.cs 中配置:
csharp
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
URL 映射示例:
| URL | Controller | Action | id |
|---|---|---|---|
/ |
Home | Index | - |
/Home/Index |
Home | Index | - |
/Home/About |
Home | About | - |
/Books/Index |
Books | Index | - |
/Books/Details/5 |
Books | Details | 5 |
6.2 特性路由
在 Controller 或 Action 上使用特性定义路由。
csharp
[Route("api/[controller]")]
[ApiController]
public class BooksController : Controller
{
[HttpGet]
[Route("all")]
public IActionResult GetAll()
{
return View();
}
[HttpGet("{id:int}")]
[Route("details/{id}")]
public IActionResult Details(int id)
{
return View();
}
}
七、完整示例:图书管理
7.1 创建模型
csharp
// Models/Book.cs
using System.ComponentModel.DataAnnotations;
public class Book
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
[Required]
public string Author { get; set; }
[Range(0.01, 9999.99)]
public decimal Price { get; set; }
}
7.2 创建控制器
csharp
// Controllers/BooksController.cs
using Microsoft.AspNetCore.Mvc;
using MyMvcApp.Models;
public class BooksController : Controller
{
private static List<Book> books = new List<Book>
{
new Book { Id = 1, Title = "C# 入门", Author = "张三", Price = 89.5m },
new Book { Id = 2, Title = "ASP.NET Core", Author = "李四", Price = 99.0m }
};
// GET: Books
public IActionResult Index()
{
return View(books);
}
// GET: Books/Details/5
public IActionResult Details(int id)
{
var book = books.FirstOrDefault(b => b.Id == id);
if (book == null)
{
return NotFound();
}
return View(book);
}
// GET: Books/Create
public IActionResult Create()
{
return View();
}
// POST: Books/Create
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Book book)
{
if (ModelState.IsValid)
{
book.Id = books.Max(b => b.Id) + 1;
books.Add(book);
return RedirectToAction(nameof(Index));
}
return View(book);
}
// GET: Books/Edit/5
public IActionResult Edit(int id)
{
var book = books.FirstOrDefault(b => b.Id == id);
if (book == null)
{
return NotFound();
}
return View(book);
}
// POST: Books/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Book book)
{
if (id != book.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
var existing = books.FirstOrDefault(b => b.Id == id);
if (existing != null)
{
existing.Title = book.Title;
existing.Author = book.Author;
existing.Price = book.Price;
}
return RedirectToAction(nameof(Index));
}
return View(book);
}
// GET: Books/Delete/5
public IActionResult Delete(int id)
{
var book = books.FirstOrDefault(b => b.Id == id);
if (book == null)
{
return NotFound();
}
return View(book);
}
// POST: Books/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public IActionResult DeleteConfirmed(int id)
{
var book = books.FirstOrDefault(b => b.Id == id);
if (book != null)
{
books.Remove(book);
}
return RedirectToAction(nameof(Index));
}
}
7.3 创建视图
html
<!-- Views/Books/Index.cshtml -->
@model List<Book>
@{
ViewData["Title"] = "图书列表";
}
<h1>图书列表</h1>
<p>
<a asp-action="Create" class="btn btn-primary">添加新书</a>
</p>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>书名</th>
<th>作者</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.Id</td>
<td>@item.Title</td>
<td>@item.Author</td>
<td>@item.Price</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">编辑</a> |
<a asp-action="Details" asp-route-id="@item.Id">详情</a> |
<a asp-action="Delete" asp-route-id="@item.Id">删除</a>
</td>
</tr>
}
</tbody>
</table>
八、本周小结
核心知识点
-
MVC 模式
- Model(模型):数据和业务逻辑
- View(视图):用户界面
- Controller(控制器):控制逻辑
-
Razor 语法
- @ 输出变量
- @{} 代码块
- @if、@foreach 等控制结构
-
数据传递
- ViewData、ViewBag、TempData
- 强类型模型
-
路由
- 约定路由
- 特性路由
实践成果
✅ 创建了 MVC 项目
✅ 理解了 MVC 各组件的职责
✅ 掌握了 Razor 基本语法
✅ 实现了图书管理 CRUD 页面