diff --git "a/\351\231\210\346\201\251\347\224\237/20260112-MVC JS\350\247\206\345\233\276\344\272\244\344\272\222.md" "b/\351\231\210\346\201\251\347\224\237/20260112-MVC JS\350\247\206\345\233\276\344\272\244\344\272\222.md" new file mode 100644 index 0000000000000000000000000000000000000000..adedd368b309d763e9ac0c1fde4291e2d1d6d3dd --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260112-MVC JS\350\247\206\345\233\276\344\272\244\344\272\222.md" @@ -0,0 +1,199 @@ +# 笔记部分 + +## MVC 模式核心角色回顾 + +**在前端 MVC 中:** + +- Model(模型):负责数据管理(存储、验证、请求),不关心视图; + +- View(视图):负责页面展示,只做渲染和绑定事件,不处理业务逻辑; + +- Controller(控制器):作为中间层,接收视图的事件、操作模型,再通知视图更新。 + +## 完整的 JS MVC 与视图交互示例 + +**下面通过一个「待办事项(Todo)」的例子,完整展示视图与控制器、模型的交互逻辑,所有代码可直接在 HTML 文件中运行:** + +```html + + + + + MVC 视图交互示例 + + + +
+ + +
+
+ + + + +``` + +## 核心交互逻辑解释 + +- 视图 → 控制器 → 模型: + + - 视图只负责绑定事件(如点击「添加」按钮),不处理逻辑,而是把事件交给控制器的 handleAddTodo 方法; + + - 控制器接收视图的事件后,调用模型的 addTodo 方法修改数据; + + - 模型修改数据后,通过「订阅 - 发布」模式通知所有订阅者(这里是控制器)。 + +- 模型 → 控制器 → 视图: + + - 控制器提前订阅了模型的变化,模型更新时会触发订阅的回调; + + - 回调中控制器调用视图的 renderTodos 方法,让视图重新渲染页面。 + +- 关键设计点: + + - 视图不直接操作模型,模型不直接接触视图,所有交互都通过控制器中转; + + - 模型使用「订阅 - 发布」模式解耦,避免模型直接依赖视图; + + - 视图使用「事件委托」处理动态生成的元素(如删除按钮),减少事件绑定开销。 + +## 总结 + +- 交互核心:视图只做「展示 + 事件绑定」,模型只做「数据管理」,控制器作为中间层承接视图事件、操作模型,并通知视图更新; + +- 解耦关键:通过「订阅 - 发布」让模型变化通知视图,避免模型和视图直接耦合; + +- 最佳实践:视图事件使用委托绑定,模型返回数据副本,控制器只处理业务逻辑,不掺杂渲染或数据操作。 \ No newline at end of file diff --git "a/\351\231\210\346\201\251\347\224\237/20260114-MVC\346\250\241\345\236\213\357\274\210Model\357\274\211\347\232\204\344\275\234\347\224\250.md" "b/\351\231\210\346\201\251\347\224\237/20260114-MVC\346\250\241\345\236\213\357\274\210Model\357\274\211\347\232\204\344\275\234\347\224\250.md" new file mode 100644 index 0000000000000000000000000000000000000000..5ecf44037564d3ea395db66a199a3edebccb31f8 --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260114-MVC\346\250\241\345\236\213\357\274\210Model\357\274\211\347\232\204\344\275\234\347\224\250.md" @@ -0,0 +1,185 @@ +# MVC 中「模型(Model)」的核心作用 & 完整详解 + +**你想了解 MVC 架构里最核心的Model(模型)的具体作用,这是理解 MVC 设计思想的关键,我会把它的核心定位、全部核心作用、特性、与其他层的关系讲清楚,内容易懂且完整。** + +## 一、Model 的「核心定位」(一句话总结) + +**Model(模型)是 MVC 三层架构的`核心层、数据层、业务逻辑层`,是整个应用的「大脑」和「数据中心」,是 MVC 架构的基石。** + +补充:MVC 三层 = Model (模型) + View (视图) + Controller (控制器) + +## 二、Model 承担的「3 个核心作用」(重中之重,必记) + +**这是 Model 的核心职责,`所有功能都围绕这三点展开`,职责边界清晰且固定:** + +### ✅ 作用 1:封装「应用的核心业务逻辑 & 业务规则」 + +**这是 Model 最核心的职责,`没有之一`。** + +- 业务逻辑:指系统的核心业务处理流程,比如「用户注册时的手机号校验 + 密码加密 + 重复注册判断」、「商品下单时的库存扣减 + 价格计算 + 订单状态更新」、「支付时的金额校验 + 支付状态同步」等; + +- 业务规则:指业务的约束条件,比如「用户年龄必须≥18 岁」、「商品库存不能为负数」、「订单支付超时自动取消」等; + +- 核心特点:`所有和业务相关的判断、计算、规则校验,全部放在 Model 中`,View/Controller 层`绝对不写任何业务逻辑`。 + +### ✅ 作用 2:封装「数据管理」的全生命周期(数据的增删改查) + +**Model 是应用中`唯一负责数据处理`的组件,统一管理应用的所有数据,包含:** + +- 数据的来源:数据库(MySQL/Redis)、本地缓存、第三方接口(支付接口 / 地图接口)、本地文件等; + +- 数据的操作:统一提供「增 (Create)、删 (Delete)、改 (Update)、查 (Read)」的方法,比如 saveUser() 保存用户、getGoodsById() 根据 ID 查商品、updateStock() 更新库存; + +- 数据的维护:数据的格式转换、数据校验、数据缓存等,比如把数据库查询的原始数据转为前端需要的格式,把用户输入的非法数据过滤。 + +### ✅ 作用 3:封装「数据的状态」,并提供统一的状态访问方式 + +- 应用中所有的数据状态都由 Model 持有和维护,比如「当前登录用户的信息」、「购物车的商品列表」、「订单的支付状态」、「商品的库存数量」等; + +- Model 会对外暴露`只读 / 可写的访问接口`,其他层(View/Controller)想要获取或修改数据,`必须通过 Model 提供的接口完成`,不能直接操作 Model 内部的数据。 + +## 三、Model 的「2 个核心特性」(决定了它的设计原则) + +**Model 能成为 MVC 的核心,正是因为这两个特性,也是开发中必须遵守的设计准则,缺一不可:** + +### ✅ 特性 1:纯独立的业务组件,完全解耦 View 和 Controller + +**Model 层是「无感知」的:** + +- Model 不知道「谁在调用自己」—— 它不知道有 View(视图)的存在,也不知道有 Controller(控制器)的存在; + +- Model 不关心「数据要展示成什么样」—— 它只负责处理数据和业务,不负责数据的可视化渲染(比如页面样式、按钮位置、表格布局都和 Model 无关); + +- 核心价值:解耦后,`修改视图(比如把网页改成小程序、把按钮改成下拉框),完全不需要改动 Model 代码`;同理,修改业务逻辑,也不会影响视图展示,极大提升代码的可维护性。 + +### ✅ 特性 2:不包含任何和「界面 / 视图」相关的代码 + +**这是 Model 的「红线准则」:** + +- Model 内部`绝对不能写`:页面跳转、弹窗提示、DOM 操作、样式修改、表单渲染等和「视图展示」相关的代码; + +- 为什么?如果 Model 掺杂了视图逻辑,会导致代码耦合严重,后期改一个业务规则,可能要改多个视图相关的代码,牵一发而动全身。 + +## 四、Model 和 View/Controller 的关系(清晰职责边界) + +**MVC 的核心价值就是`「各司其职、职责分离」`,三层的关系可以总结为:`Model 只做自己的事,View 和 Controller 都依赖 Model,但彼此互不干扰`,用三句话讲清楚:** + +### 1. View(视图) ← 依赖 → Model(模型) + +- View 是「展示层」,只负责:`把 Model 提供的数据,以可视化的形式展示给用户`(比如页面、弹窗、列表); + +- View 不能直接操作数据,想要什么数据,就向 Model 要;想要修改数据,必须通过 Model 提供的方法; + +- 举个例子:页面要展示「商品列表」,View 只需要调用 Model 的 getGoodsList() 方法获取数据,然后渲染成页面即可,至于数据从哪查、怎么过滤,View 完全不用管。 + +### 2. Controller(控制器) ← 中转 → Model + View + +- Controller 是「调度层、中转站」,是 Model 和 View 之间的唯一桥梁,它的核心职责只有 2 个: + + - ① 接收用户的操作(比如点击按钮、提交表单、输入搜索内容); + + - ② 调用 Model 的方法处理业务 / 数据,再把处理后的结果,交给 View 去展示; + +- Controller 本身`不存数据、不写业务逻辑、不负责展示`,就是一个「指令传递者」; + +- 举个例子:用户点击「下单按钮」→ Controller 接收这个操作 → 调用 Model 的 createOrder() 方法(处理下单业务、扣库存、生成订单)→ Model 返回「下单成功 / 失败」→ Controller 让 View 展示「下单成功弹窗」或「库存不足提示」。 + +### 3. 终极原则:Model 不依赖任何一层 + +**Model 是整个架构的「核心」,它的代码只关注「业务和数据」,不依赖 View,也不依赖 Controller。** + +- 无论 View 是网页还是 APP,无论 Controller 是前端控制器还是后端控制器,Model 的代码都不需要任何修改,可以直接复用; + +- 这也是 MVC 最核心的设计思想:`让核心的业务逻辑,不受任何外部因素的影响`。 + +## 五、Model 的核心设计原则(开发中必须遵守) + +**结合上面的作用和特性,总结 3 个最核心的设计原则,记住这 3 点,就能写出标准的 Model 层代码:** + +### ✅ 原则 1:单一职责 + +一个 Model 只负责「一个业务模块」的逻辑和数据,比如:`UserModel` 只处理用户相关(登录、注册、修改信息)、`GoodsModel` 只处理商品相关(查商品、改库存、加分类)、OrderModel 只处理订单相关(下单、查订单、取消订单)。 + +### ✅ 原则 2:数据封装与访问控制 + +Model 内部的数据(比如用户信息、商品库存)要「私有化」,不允许外部直接访问和修改,必须通过 Model 暴露的「公开方法」操作: + +- 比如:不能直接 `userModel.name = "张三"`,而是要调用 `userModel.updateName("张三")`; + +- 比如:不能直接 `goodsModel.stock--`,而是要调用 `goodsModel.reduceStock()`; +这样做的目的:保证数据的安全性,所有数据修改都经过 Model 的业务校验,不会出现「库存为负数」「用户信息非法修改」的问题。 + +### ✅ 原则 3:高内聚、低耦合 + +- 高内聚:把和同一个业务相关的逻辑、数据、方法,都放在同一个 Model 中,比如「用户注册、登录、修改密码」都在 UserModel 里,代码关联性强; + +- 低耦合:Model 和其他层之间,只通过「接口」交互,没有硬编码依赖,比如修改 View 不会影响 Model,修改 Controller 也不会影响 Model。 + +## 六、一句话总结所有核心内容(方便记忆) + +### ✅ Model 是 MVC 的「数据中心 + 业务大脑」,只负责封装业务逻辑、管理数据生命周期、维护数据状态,不依赖视图、不关心展示,是整个应用最稳定、最核心、可复用性最强的一层。 + +- 补充:简单代码示例(前端 JS 版,一看就懂) + +**用极简的代码演示三层分工,直观感受 Model 的作用:** + +```javascript +// ===== 1. Model 层(核心:业务+数据,无任何视图/控制器代码) +class GoodsModel { + // 数据:商品库存(私有化,外部不能直接修改) + #stock = 10; + + // 业务逻辑:扣减库存(包含规则校验) + reduceStock(num) { + if (num <= 0) return { success: false, msg: "数量非法" }; + if (this.#stock < num) return { success: false, msg: "库存不足" }; + this.#stock -= num; + return { success: true, msg: "扣减成功", stock: this.#stock }; + } + + // 提供数据访问接口:获取当前库存 + getStock() { + return this.#stock; + } +} + +// ===== 2. View 层(只负责展示,无业务逻辑) +class GoodsView { + showStock(stock) { + console.log(`当前库存:${stock}`); + } + showMsg(msg) { + console.log(`【提示】${msg}`); + } +} + +// ===== 3. Controller 层(中转站,只调度) +class GoodsController { + constructor() { + this.model = new GoodsModel(); + this.view = new GoodsView(); + } + + // 接收用户操作:点击「购买按钮」 + buyGoods(num) { + const result = this.model.reduceStock(num); // 调用Model处理业务 + this.view.showMsg(result.msg); // 调用View展示结果 + if (result.success) { + this.view.showStock(result.stock); // 展示最新库存 + } + } +} + +// 测试 +const controller = new GoodsController(); +controller.buyGoods(2); // 【提示】扣减成功 | 当前库存:8 +controller.buyGoods(9); // 【提示】库存不足 +``` + +## 总结 + +- Model 的核心是「业务 + 数据」,是 MVC 的绝对核心; + +- Model 最大的价值是「解耦」,让业务逻辑独立、稳定、可复用; + +- 三层分工的核心:Model 做什么,View 展示什么,Controller 传什么。 \ No newline at end of file diff --git "a/\351\231\210\346\201\251\347\224\237/20260115-MVC Model.md" "b/\351\231\210\346\201\251\347\224\237/20260115-MVC Model.md" new file mode 100644 index 0000000000000000000000000000000000000000..ec5b9636fb5dd222e45cc68f088d9dfed9f739d0 --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260115-MVC Model.md" @@ -0,0 +1,282 @@ +# MVC 中的 Model + +**在 MVC(Model-View-Controller)架构中,Model(模型)是应用程序的核心,它:** + +- 封装应用程序的数据(属性) + +- 包含处理这些数据的业务逻辑(方法) + +- 不依赖于视图(View)和控制器(Controller),保证数据逻辑的独立性 + +## 创建 Model 类的实战示例 + +**下面以最常用的`用户信息管理`为例,用 Java 语言演示一个标准的 Model 类创建(其他语言如 C#、Python 的思路完全一致):** + +### Java 版本 + +```java +/** + * 用户模型类 - MVC中的Model层 + * 负责封装用户数据和相关业务逻辑 + */ +public class User { + // 1. 封装数据(私有属性) + private Long id; // 用户ID + private String username; // 用户名 + private String password; // 密码(实际项目中应存储加密后的密码) + private String email; // 邮箱 + private int age; // 年龄 + + // 2. 无参构造方法(框架反射、序列化常用) + public User() { + } + + // 3. 有参构造方法(快速创建对象) + public User(Long id, String username, String password, String email, int age) { + this.id = id; + this.username = username; + this.password = password; + this.email = email; + this.age = age; + } + + // 4. Getter/Setter方法(访问和修改私有属性) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + // 业务逻辑校验:年龄必须大于0 + if (age > 0) { + this.age = age; + } else { + throw new IllegalArgumentException("年龄必须大于0"); + } + } + + // 5. 业务逻辑方法(模型的核心价值) + /** + * 验证用户密码是否正确 + * @param inputPassword 输入的密码 + * @return 验证结果 + */ + public boolean validatePassword(String inputPassword) { + // 实际项目中应使用加密对比,此处简化演示 + return this.password.equals(inputPassword); + } + + /** + * 检查用户是否成年 + * @return 是否成年 + */ + public boolean isAdult() { + return this.age >= 18; + } + + // 6. 重写toString(方便调试和日志输出) + @Override + public String toString() { + return "User{" + + "id=" + id + + ", username='" + username + '\'' + + ", email='" + email + '\'' + + ", age=" + age + + '}'; + } +} +``` + +**不同语言的 Model 示例(补充)** + +### Python 版本 + +```python +class User: + """用户模型类 - MVC中的Model层""" + def __init__(self, id: int = None, username: str = None, password: str = None, email: str = None, age: int = 0): + self.id = id + self.username = username + self.password = password + self.email = email + self.age = age + + # 属性封装(可选,Python支持直接访问属性) + @property + def age(self): + return self._age + + @age.setter + def age(self, value): + if value > 0: + self._age = value + else: + raise ValueError("年龄必须大于0") + + # 业务逻辑方法 + def validate_password(self, input_password: str) -> bool: + """验证密码""" + return self.password == input_password + + def is_adult(self) -> bool: + """检查是否成年""" + return self.age >= 18 + + def __str__(self): + return f"User(id={self.id}, username='{self.username}', email='{self.email}', age={self.age})" +``` + +### C# 版本 + +```csharp +/// +/// 用户模型类 - MVC中的Model层 +/// +public class User +{ + // 私有字段 + private long _id; + private string _username; + private string _password; + private string _email; + private int _age; + + // 公共属性 + public long Id + { + get => _id; + set => _id = value; + } + + public string Username + { + get => _username; + set => _username = value; + } + + public string Password + { + get => _password; + set => _password = value; + } + + public string Email + { + get => _email; + set => _email = value; + } + + public int Age + { + get => _age; + set + { + if (value > 0) + _age = value; + else + throw new ArgumentException("年龄必须大于0"); + } + } + + // 构造函数 + public User() { } + + public User(long id, string username, string password, string email, int age) + { + Id = id; + Username = username; + Password = password; + Email = email; + Age = age; + } + + // 业务逻辑方法 + public bool ValidatePassword(string inputPassword) + { + return Password == inputPassword; + } + + public bool IsAdult() + { + return Age >= 18; + } + + // 重写ToString + public override string ToString() + { + return $"User {{ Id = {Id}, Username = '{Username}', Email = '{Email}', Age = {Age} }}"; + } +} +``` + +### 模型类的使用示例(Java) + +```java +public class ModelDemo { + public static void main(String[] args) { + // 创建模型对象 + User user = new User(1L, "zhangsan", "123456", "zhangsan@example.com", 20); + + // 使用模型的业务方法 + System.out.println("用户是否成年:" + user.isAdult()); // 输出:true + System.out.println("密码验证结果:" + user.validatePassword("123456")); // 输出:true + + // 尝试设置非法年龄 + try { + user.setAge(-5); + } catch (IllegalArgumentException e) { + System.out.println("错误:" + e.getMessage()); // 输出:错误:年龄必须大于0 + } + + // 打印用户信息 + System.out.println(user); // 输出:User{id=1, username='zhangsan', email='zhangsan@example.com', age=20} + } +} +``` + +## 总结 + +- `Model 类核心特征`:封装数据(私有属性 + Getter/Setter)、包含业务逻辑(数据校验、业务计算等方法)、独立于 View/Controller。 + +- `最佳实践`: + + - 私有属性 + 公共访问方法(封装性) + + - 包含必要的构造方法(无参 + 有参) + + - 数据校验逻辑放在 Model 层(而非 Controller) + + - 重写 toString 方法(方便调试) + +- `核心原则`:Model 只负责数据和业务逻辑,不处理界面展示(View)和用户交互(Controller),保证单一职责。 \ No newline at end of file diff --git "a/\351\231\210\346\201\251\347\224\237/20260116-MVC\346\225\260\346\215\256\346\263\250\350\247\243.md" "b/\351\231\210\346\201\251\347\224\237/20260116-MVC\346\225\260\346\215\256\346\263\250\350\247\243.md" new file mode 100644 index 0000000000000000000000000000000000000000..c31d1a1c690e877f12b356f2bb2be0d8373127d1 --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260116-MVC\346\225\260\346\215\256\346\263\250\350\247\243.md" @@ -0,0 +1,163 @@ +# 数据注解(Data Annotations) + +## 一、数据注解的核心作用 + +**数据注解是一组特性(Attribute),主要用于:** + +- `数据验证`:在用户提交表单 / 数据时,自动校验数据的合法性(如必填、长度、格式等)。 + +- `元数据描述`:控制数据在视图(View)中的显示方式(如字段名称、显示格式、是否隐藏等)。 + +- `数据库映射`:配合 Entity Framework 时,还能定义数据库表 / 列的特性(如主键、列名、是否可为空等)。 + +## 二、常用数据注解分类及示例 + +**下面结合实际的 MVC 模型类,展示最常用的注解,你可以直接复制到项目中使用。** + +### 1. 基础验证注解(最常用) + +```csharp +using System.ComponentModel.DataAnnotations; + +// 示例:用户注册模型 +public class UserRegisterModel +{ + // 1. 必填项(不允许为空) + [Required(ErrorMessage = "用户名不能为空")] + // 字符串长度限制(最小2位,最大20位) + [StringLength(20, MinimumLength = 2, ErrorMessage = "用户名长度必须在2-20个字符之间")] + // 自定义显示名称(视图中显示“用户名”而非“UserName”) + [Display(Name = "用户名")] + public string UserName { get; set; } + + // 2. 邮箱格式验证 + [Required(ErrorMessage = "邮箱不能为空")] + [EmailAddress(ErrorMessage = "请输入有效的邮箱地址")] + [Display(Name = "邮箱")] + public string Email { get; set; } + + // 3. 密码验证(正则+比较) + [Required(ErrorMessage = "密码不能为空")] + // 正则表达式:至少8位,包含大小写字母和数字 + [RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$", ErrorMessage = "密码至少8位,包含大小写字母和数字")] + [Display(Name = "密码")] + [DataType(DataType.Password)] // 视图中渲染为密码输入框(隐藏输入内容) + public string Password { get; set; } + + // 4. 密码确认(和Password字段值比较) + [Compare("Password", ErrorMessage = "两次输入的密码不一致")] + [Display(Name = "确认密码")] + [DataType(DataType.Password)] + public string ConfirmPassword { get; set; } + + // 5. 数值范围验证 + [Range(18, 120, ErrorMessage = "年龄必须在18-120岁之间")] + [Display(Name = "年龄")] + public int Age { get; set; } + + // 6. 自定义验证(示例:手机号格式) + [CustomValidation(typeof(UserRegisterModel), "ValidatePhone")] + [Display(Name = "手机号")] + public string Phone { get; set; } + + // 自定义验证方法 + public static ValidationResult ValidatePhone(string phone, ValidationContext context) + { + if (string.IsNullOrEmpty(phone)) + return ValidationResult.Success; // 非必填,空则通过 + + // 手机号正则(11位数字,以1开头) + var regex = new System.Text.RegularExpressions.Regex(@"^1[3-9]\d{9}$"); + if (!regex.IsMatch(phone)) + return new ValidationResult("请输入有效的手机号"); + + return ValidationResult.Success; + } +} +``` + +### 2. 控制器中触发验证 + +**在 MVC 控制器中,只需检查 ModelState.IsValid 即可判断数据是否通过注解验证:** + +```csharp +public class UserController : Controller +{ + [HttpPost] + public ActionResult Register(UserRegisterModel model) + { + // 检查数据注解验证结果 + if (ModelState.IsValid) + { + // 验证通过,执行注册逻辑 + return RedirectToAction("Success"); + } + + // 验证失败,返回原视图并显示错误信息 + return View(model); + } +} +3. 视图中显示验证错误 +在 Razor 视图中,可通过内置标签显示验证错误: +html +预览 +@model UserRegisterModel + + +@if (!ViewData.ModelState.IsValid) +{ +
+ 表单填写有误,请检查后重新提交 +
+} + + +
+ @Html.LabelFor(m => m.UserName) + @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) + @Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" }) +
+ + +
+ @Html.LabelFor(m => m.Password) + @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) + @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" }) +
+ + + +``` + +## 三、进阶用法:数据库映射注解 + +**配合 Entity Framework 时,数据注解还能控制数据库表结构:** + +```csharp +using System.ComponentModel.DataAnnotations.Schema; + +public class User +{ + // 主键(自增) + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + + // 列名自定义(数据库中列名为"User_Name") + [Column("User_Name")] + [MaxLength(20)] // 数据库列长度 + public string UserName { get; set; } + + // 不映射到数据库(忽略此字段) + [NotMapped] + public string TempData { get; set; } +} +``` + +## 总结 + +- `核心价值`:数据注解通过声明式语法替代手动编写验证逻辑,让 MVC 模型层兼具 “数据定义” 和 “规则验证” 能力,减少重复代码。 + +- `核心注解`:[Required](必填)、[StringLength](长度)、[EmailAddress](邮箱)、[Range](数值范围)、[Compare](字段比较)是最常用的验证注解。 + +- `使用流程`:模型中加注解 → 控制器检查 ModelState.IsValid → 视图显示验证错误,三步完成数据校验。 \ No newline at end of file