ValidationsAndTransformations
🏗️ 后端分层架构
什么是分层架构?
分层架构是一种后端设计模式,其中应用程序分为不同的层,每个层都有特定的职责。
1 | Client → Controller → Service → Repository → Database |
它确保干净的代码、关注点分离和可扩展性。
为什么使用它?
它解决的问题
- 将业务逻辑与 HTTP 逻辑混合
- 控制器内的 SQL 查询
- 难以维护且混乱的代码
- 测试困难
- 紧耦合
好处
- 结构简洁
- 轻松测试
- 可扩展的设计
- 可维护的代码
- 团队友好
层解释
1️⃣ 控制器层
作用: 处理 HTTP 请求。
职责:
- 验证输入
- 验证/授权
- 致电服务
- 返回响应
用于: 仅请求级验证。
2️⃣ 服务层
作用: 包含业务逻辑。
职责:
- 应用业务规则
- 协调存储库
- 数据转换
用途: 业务验证(例如,“用户已存在”)。
3️⃣ 存储库层
作用: 数据库交互。
职责:
- 增删改查操作
- 与DB / ORM通信
用途: 所有数据库访问。
验证规则
| 类型 | 层 |
|---|---|
| 输入验证 | 控制器 |
| 业务验证 | 服务 |
何时使用
推荐用于:
- API
- 可扩展的后端系统
- 生产项目
- 团队环境
不需要:
- 小脚本
- 简单的 MVP
- 单文件应用程序
优点和缺点
优点
- 干净的分离
- 可重用逻辑
- 轻松测试
- 可扩展
缺点
- 更多文件
- 样板稍微多一些
- 对于小型应用程序来说太过分了
总结
分层架构=
控制器→服务→存储库
每一层都有单一的职责,使后端变得干净、结构化和可扩展。
为什么要验证?
验证可确保传入数据在到达您的业务逻辑或数据库之前是正确、安全且符合预期的。
如果没有验证会发生什么?
示例:
您的数据库期望:
1 | age: number |
但用户发送:
1 | age: "twenty" |
结果:
- 数据库查询失败
- 应用程序崩溃
- 服务器返回 500 内部服务器错误
- 糟糕的用户体验
- 更难的调试
这意味着错误被发现得太晚了(在服务器内部)。
更好的方法:尽早验证
验证控制器层(入口点)的输入。
如果收到无效数据:
- 立即停止请求
- 返回 400 错误请求
- 发送有用的错误消息
响应示例:
1 | { |
验证解决什么问题
- 防止服务器崩溃
- 保护数据库完整性
- 提高安全性
- 改善用户体验
- 节省服务器资源
- 使调试更容易
经验法则
尽早验证。
永远不要相信客户的输入。
始终假设:
- 用户可能发送错误的数据
- 可以绕过前端验证
- API 可公开访问
总结
未经验证 → ❌ 500 内部服务器错误
经过验证 → ✅ 400 错误请求(干净且受控)
验证使您的后端安全、稳定且可用于生产。
验证类型
验证不仅仅是检查字段是否存在 - 它确保数据在格式、含义、类型和业务条件方面正确。
1️⃣ 语法验证(格式检查)
检查数据是否遵循所需的模式或结构。
它验证什么
- 电子邮件格式
- 电话号码格式
- 网址格式
- 密码图案
- 基于正则表达式的规则
示例
电子邮件验证
1 | { |
失败 — 缺少 @ 和域。
电话号码验证
1 | { |
失败 — 不是有效的国际电话格式。
密码模式
1 | { |
失败 — 不符合要求(例如,8 个以上字符、1 个特殊符号)。
2️⃣ 语义验证(逻辑/现实世界的含义)
检查数据在逻辑上是否有意义。
它的格式可能正确,但仍然是错误的。
示例
未来的出生日期
1 | { |
失败——出生日期不能是未来的日期。
负产品价格
1 | { |
失败——价格不能为负。
年龄限制
1 | { |
失败 — 必须年满 18 岁才能注册。
3️⃣ 类型验证(数据类型检查)
确保变量类型与 API 期望的类型匹配。
示例
预期数字但收到字符串
1 | { |
失败 — 应为 number,但收到 string。
需要数组但收到字符串
1 | { |
失败 — 应为 Array<string>,但收到 string。
正确的:
1 | { |
4️⃣ 复杂/条件验证
根据其他字段值验证字段。
用于现实世界的业务规则。
示例
婚姻状况规则
1 | { |
失败 — 如果 is_married 为 true,则需要 partner_name。
折扣规则
1 | { |
失败 — 如果启用折扣,折扣百分比必须大于 0。
送货地址规则
1 | { |
失败 — 送货上门需要提供地址。
总结
| 类型 | 检查 |
|---|---|
| 句法 | 格式/图案 |
| 语义 | 现实世界的逻辑 |
| 类型 | 正确的数据类型 |
| 有条件 | 取决于其他领域 |
最终规则
良好的后端验证检查:
- 格式
- 含义
- 类型
- 业务条件
强大的验证=安全+稳定+生产就绪的后端。
🔄 转换(清理)
转换(也称为清理)在传入数据到达业务逻辑或数据库之前对其进行准备。
与验证(检查正确性)不同,转换将数据修改为安全且一致的格式。
这可以确保:
- 更干净的数据库记录
- 一致的比较
- 更少的意外错误
- 更好的安全性
为什么要转换数据?
用户输入通常是:
- 不一致
- 格式不正确
- 区分大小写
- 包含额外空格
- 类型错误
如果按原样存储,可能会导致:
- 重复记录
- 匹配问题
- 查询错误
- 安全风险
所以我们要尽早消毒。
常见转换类型
1️⃣ 铸造(类型转换)
在使用数据之前将其转换为正确的类型。
示例:查询参数始终是字符串。
1 | GET /users?page=5 |
已收到:
1 | page = "5" // string |
转换:
1 | page = 5 // number |
没有铸造:
- 分页可能会中断
- 算术运算可能会失败
2️⃣ 标准化(标准化)
使数据一致以便存储和比较。
示例:电子邮件不区分大小写。
1 | { |
转换:
1 | "user@email.com" |
步骤:
- 修剪空间
- 转换为小写
没有标准化:
"User@email.com"和"user@email.com"可以被视为不同的用户。
3️⃣ 格式化(重组数据)
保存前调整结构。
示例:电话号码格式。
输入:
1 | 9876543210 |
转换:
1 | +919876543210 |
其他例子:
- 将日期格式化为 ISO 格式 (
YYYY-MM-DD) - 从用户名中删除特殊字符
- 存储前对密码进行哈希处理
4️⃣ 修剪和清洁
删除不需要的字符。
例子:
1 | { |
转换:
1 | "Yadnyesh" |
这可以防止不必要的数据库不一致。
重要规则
验证 → 然后转换 → 然后处理
- 验证输入
- 清理/转换数据
- 将干净的数据传递给服务层
总结
| 行动 | 目的 |
|---|---|
| 验证 | 检查数据是否正确 |
| 转型 | 使数据干净一致 |
清理可确保您的后端保持:
- 可预测的
- 安全
- 一致
- 生产就绪
前端与后端:黄金法则
| 特色 | 前端验证 | 后端验证 |
|---|---|---|
| 主要目标 | 用户体验(UX) | 安全与数据完整性 |
| 可靠性 | 低(可绕过) | 高(最终授权) |
| 工具 | HTML5、React/Vue 状态 | Joi、Zod、类验证器 |
注意: 切勿依赖前端来确保安全。如果用户通过 Postman 或 cURL 访问您的 API,您的前端检查将被完全绕过。