主题
开发规范与命名规范(建议)
提示信息
版本: 1.5 最后更新: 2024-03-12 适用对象: 开发工程师、技术负责人
统一的开发规范和命名规范有利团队协作,提高开发效率,减少错误,下边对日常开发做了简要的规范,供所有成员参考。
1. 数据库表和字段命名
在 SQL Server 中,表名推荐使用下划线分隔的小写形式order_status_log,而非无分隔符的驼峰式orderstatuslog。 以下是具体分析:
1.1 推荐理由
可读性更强
- order_status_log 通过下划线明确分隔单词,便于快速识别字段含义(如 order、status、log),尤其适合长表名或团队协作场景。
- orderstatuslog 无分隔符,需依赖命名习惯理解,可能增加阅读成本。
符合主流规范
- 多数 SQL 规范(如 Google、Airbnb)建议使用 蛇形命名法(snake_case),即小写下划线分隔。
- snake_case 是 SQL 社区事实标准( PostgreSQL/MySQL/Airbnb SQL 规范均如此),SQL Server 虽不区分大小写,但小写+下划线跨平台最稳。
避免潜在问题
- 若未来扩展字段或关联其他表,order_status_log 更易扩展(如 order_status_log_detail )。
- orderstatuslog 可能因命名模糊导致混淆(如是否包含 status或 log)。
1.2命名规范对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| order_status_log | 清晰、易读,符合 SQL 社区习惯 | 名称较长 | 复杂业务表、团队协作 |
| orderstatuslog | 简洁,适合简单场景 | 可读性差,易混淆 | 临时表、小型项目 |
1.3 其他注意事项
大小写一致性
- SQL Server 默认不区分大小写,但建议统一使用小写,避免跨平台问题(如 Linux 文件系统区分大小写)。
避免保留字 - 确保表名不与 SQL 关键字冲突(如 order是保留字,需用 [order]引用,但 order_status_log仍优于 orderstatuslog)。 - 但更好的实践是干脆别用 order 做前缀,用 sales_order_status_log 比 order_status_log 更安全。
业务前缀(可选)
- 若系统包含多个模块,可添加短前缀(如 crm_order_status_log),前缀语义明确即可,但建议控制前缀长度。
1.4 示例场景
sql
-- 推荐命名
-- 订单状态日志表
CREATE TABLE order_status_log (
log_id INTPRIMARY KEY,
order_id VARCHAR(32),
status VARCHAR(20),
change_time DATETIME
);sql
-- 可读性较差,不推荐
-- 订单状态日志表
CREATE TABLE orderstatuslog (
logid INTPRIMARY KEY,
orderid VARCHAR(32),
status VARCHAR(20),
changetime DATETIME
);1.5 总结
优先选择 order_status_log,因其符合 SQL 社区规范、可读性高,且能适应长期维护需求。若项目已有特定命名规范(如强制驼峰式),则需与团队保持一致,即遵循「存量优先」原则。
2. API 控制器命名
在 .NET6+ 的 API 项目中,控制器的命名需要遵循 RESTful 规范和 Clean Code 原则,以下是具体建议:
2.1 核心命名规范
使用 PascalCase(大驼峰式)
- 类名每个单词首字母大写,例如:CreditCardsController、OrdersController
复数形式表示资源集合
- 控制器名称应使用复数名词,表示操作的资源集合,例如:
- ProductsController(产品集合)
- OrdersController(订单集合)
- 避免单数形式,推荐
OrdersController而非OrderController,更符合 RESTful 资源集合的表达习惯。
避免动词,体现资源属性
- 控制器名应直接映射业务资源,而非操作,例如:
- ✅ CustomersController(客户资源)
- ❌ CustomerController(模糊不清)
- ❌ GetCustomersController(含动词)。
2.2 具体场景示例
| 资源类型 | 控制器命名 | 路由示例 | 说明 |
|---|---|---|---|
| 信用卡产品列表 | CreditCardProductsController | /api/creditcardproducts | 管理信用卡产品(CRUD) |
| 信用卡订单 | CreditCardOrdersController | /api/creditcardorders | 处理订单创建、状态更新 |
2.3 命名禁忌与优化
避免以下命名方式
- 动词开头:如 GetOrdersController→ 应用 HTTP 动词(GET /api/orders)。
- 缩写或模糊词汇:如 CustController→ 使用完整名称 CustomersController。
- 混合层级:如 CreditCardOrderController→ 拆分为 CreditCardOrdersController和 CreditCardController。 一致性原则
- 同一模块的控制器命名风格需统一,例如:
- 若使用 Controller后缀,所有控制器均需添加(如 ProductsController)。
- 若采用领域驱动命名(如 OrderService),需与业务逻辑层区分。
2.4 路由与控制器关联
通过 [Route]特性定义路由,确保控制器名与路由路径匹配:
C#
[ApiController]
[Route("api/[controller]")] // 路由自动解析为 /api/creditcardproducts
public class CreditCardProductsController : ControllerBase
{
// 操作方法...
}2.5 复杂场景的命名策略
2.5.1 资源嵌套
若资源存在层级关系,使用路径嵌套:
C#
[Route("api/customers/{customerId}/orders")]
public class CustomerOrdersController : ControllerBase
{
// 客户订单相关操作...
}2.5.2 版本控制
API 版本可通过路由参数区分,控制器名无需包含版本号:
C#
[Route("api/v1/creditcardproducts")]
public class CreditCardProductsController : ControllerBase { }优点:
- 最直观,用户一眼知道用哪个版本
- 缓存友好(不同 URL)
- 兼容性好,所有 HTTP 客户端都支持
- Swagger/OpenAPI 支持完善
缺点:
- URL 会变(但可以接受,因为版本升级本来就是破坏性变更)
💡 提示信息
这是业界最主流的做法,Azure、GitHub、Stripe 都用这种方式。
也可以用Header实现,但不建议。
2.6 验证工具与测试
Swagger 自动生成
- 使用 [ApiExplorerSettings] 和 Swagger 配置,确保控制器名在文档中清晰展示。
- 也可使用微软官方的 scalar ,类似Swagger。
单元测试命名
- 建议测试类名与控制器名对应,例如:
C#
public class CreditCardProductsControllerTests {
//.....
}2.7 总结
最佳实践:
- 使用 复数名词 + Controller 后缀(如 CreditCardProductsController )
- 遵循 RESTful 路由规则(/api/{controller}/{id?})
- 保持与业务领域一致,避免技术术语干扰可读性
- 通过规范化的命名,可显著提升代码可维护性和团队协作效率。
3. Action 命名规则( C# 操作方法)
在 .NET 6+ 的 Web API 项目中,控制器 (Controller) 与操作方法 (Action) 的命名遵循“约定优于配置”的原则。以下是基于 RESTful 风格的主流建议,您可以根据项目规范进行调整。
3.1 使用 PascalCase 命名
方法名应采用 PascalCase(每个单词首字母大写)格式,这是 C# 的通用命名规范。
C#
public class ProductsController : ControllerBase
{
public IActionResult GetAll() { }
public IActionResult GetById(int id) { }
}3.2 语义化命名,而非“动词+控制器名”
应避免使用 GetUserController.GetUser 这类冗余的命名方式。更推荐的做法是,仅用 Get 表示获取操作,通过 HTTP 方法和路由来区分具体资源。 对于非CRUD的业务操作,推荐使用动词 + 名词的语义化命名(如 CalculateDiscount、ValidateUser),比单纯的 Get/Post 更具可读性。
C#
[HttpGet]
public IActionResult Get() { }
[HttpGet("{id}")]
public IActionResult Get(int id) { }
[HttpPost]
public IActionResult Create([FromBody] ProductDto dto) { }
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] ProductDto dto) { }
[HttpDelete("{id}")]
public IActionResult Delete(int id) { }3.3 通过特性明确 HTTP 方法
每个 Action 方法都必须使用 [HttpGet], [HttpPost], [HttpPut], [HttpDelete]等特性来明确其支持的 HTTP 方法。不添加这些特性,框架将无法将请求正确路由到该方法。
C#
public class OrdersController : ControllerBase
{
// GET /orders
[HttpGet]
public IActionResult Get() { }
// POST /orders
[HttpPost]
public IActionResult Post([FromBody] OrderDto dto) { }
}3.4 使用 [NonAction] 标记非 Action 方法
如果控制器中存在一些辅助性的私有方法,不希望它们被暴露为 API 接口,应使用 [NonAction] 特性进行标记。
C#
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult Get(int id) { }
// 此方法不会作为 API 接口暴露
[NonAction]
private string BuildCacheKey(int id) => $"user:{id}";
}3.5 显式指定 Action 名称
如果需要使用 RPC 风格的 URL(如 /api/products/GetProductById ),可以在 Action 上使用 [Route] 特性来显式指定路径。虽然此操作没有问题,但不符合 RESTful 风格,只要团队内统一规范,混合使用本身不是问题。
C#
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// GET /api/products/GetProductById/1
[HttpGet("GetProductById/{id}")]
public IActionResult GetProductById(int id) { }
}3.6总结
| 类型 | 规范 | 示例 |
|---|---|---|
| 类名、实体名 | 大驼峰 PascalCase | UserInfo |
| 属性 | 大驼峰 PascalCase | CreateTime |
| 方法名 | 大驼峰 PascalCase | GetUserList |
| 方法参数 | 小驼峰 camelCase,绝对不能用下划线,绝对不能用大驼峰 | userId |
| 局部变量 | 小驼峰 camelCase,第一个单词小写,后面单词首字母大写,不使用下划线,除了私有字段可以使用下划线,其他都不建议,甚至是不应该,这是.NET社区广泛采用的约定,能快速区分成员变量与局部变量。虽然微软官方说“可选”,但 Roslyn/StyleCop 默认推荐 _ 前缀。 | userList,private readonly ILogger _logger;private string _userName; |
| 常量 | 大驼峰 PascalCase |
4.Vue3 前端命名
在 Vue3 前端项目中,路由命名建议遵循 一致性、语义化、可维护性 原则,结合行业规范和实际业务场景,以下是具体建议和最佳实践:
4.1 核心命名规范
4.1.1 路由名称( name 属性)
命名风格:使用 PascalCase(大驼峰式),与组件名保持一致
js
// 推荐
{ path: '/user', name: 'UserProfile', component: UserProfile }
// 不推荐
{ path: '/user', name: 'user_profile' }唯一性:全局唯一,避免重复(可通过 ESLint 插件 eslint-plugin-vue-router 检查)
语义化:反映页面功能,如 OrderList 而非 Page1
4.1.2 路由路径( path 属性)
命名风格:使用 kebab-case(短横线分隔小写),符合 URL 标准\
js
// 推荐
path: '/user-profile/edit/:id'
// 不推荐
path: '/UserProfile/Edit/:id' 或 path: '/user_profile_edit/:id'// 动态参数:动态段使用 camelCase(小驼峰,JS 中访问更直观)
path: '/user/:userId/detail'// JS 中通过 params.userId 获取4.1.3 嵌套路由与命名视图
嵌套路由:父级路由名称建议包含模块名,子级路由体现层级关系
js
{
path: '/system',
name: 'System',
children: [
{ path: 'user', name: 'SystemUser', component: SystemUser }
]
}命名视图:通过 name 属性区分多视图区域(如顶部导航栏、侧边栏)
html
<router-viewname="sidebar"></router-view>
<router-viewname="main"></router-view>4.2 目录结构与模块化
4.2.1 路由配置拆分
按功能模块拆分路由文件,提升可维护性
bash
src/
├── router/
│ ├── index.js # 主路由入口
│ ├── static.js # 静态路由(如登录页)
│ ├── dynamic.js # 动态路由(需权限加载)
│ └── modules/
│ ├── user.js # 用户模块路由
│ └── order.js # 订单模块路由4.2.2 组件与路由命名对应
页面组件文件名与路由名称保持一致(PascalCase)
bash
src/
└── views/
├── UserProfile.vue # 对应路由名称 UserProfile
└── OrderDetail.vue # 对应路由名称 OrderDetail---4.3 最佳实践示例
4.3.1 用户管理模块
js
// router/modules/user.js
exportdefault [
{
path: '/user',
name: 'UserList',
component: () =>import('@/views/UserList.vue'),
meta: { title: '用户列表' }
},
{
path: '/user/:userId/detail',
name: 'UserDetail',
component: () =>import('@/views/UserDetail.vue'),
meta: { title: '用户详情' }
}
]4.3.2 订单管理模块
js
// router/modules/order.js
exportdefault [
{
path: '/order',
name: 'OrderList',
component: () =>import('@/views/OrderList.vue'),
meta: { title: '订单列表' }
},
{
path: '/order/:orderId/edit',
name: 'OrderEdit',
component: () =>import('@/views/OrderEdit.vue'),
meta: { title: '编辑订单' }
}
]4.4 避坑指南
4.4.1 常见错误
- 名称不匹配:路由名称与组件名不一致导致缓存失效(Vue3-Element-Admin 中需严格一致)
- 动态参数混淆:路径参数使用下划线(如 :user_id)而非 camelCase,导致 JS 中访问困难
- 重复命名:不同模块使用相同路由名称,引发跳转冲突
4.4.2 解决方案
- ESLint 约束:配置 vue-router/route-name-casing(PascalCase)和 vue-router/route-path-casing(kebab-case),从工具层面防止命名错误。
- 模块前缀:中大型系统路由 name 添加模块前缀(如 UserList、OrderDetail),避免跨模块重名冲突。
- 路由索引:在 docs/ 下维护简易路由映射表,记录路由用途与负责人,方便协作排查。
4.5 工具与配置建议
ESLint 规则
js
// 前置:npm install -D eslint-plugin-vue-router,并在 plugins 里注册 'vue-router'。
// .eslintrc.js
rules: {
'vue-router/route-name-casing': ['error', 'PascalCase'],
'vue-router/route-path-casing': ['error', 'kebab-case'],
}路由检查脚本
bash
# 路由检查脚本:检测重复 name
grep -rohE "name:\s*['\"][^'\"]+['\"]" "${1:-src/router}" | \
sed -E "s/name:\s*['\"]([^'\"]+)['\"]/\1/" | sort | uniq -d4.6 总结
命名规范的核心目标是提升代码可读性和协作效率。
建议采用:
- 路由名称:PascalCase(如 UserProfile)
- 路由路径:kebab-case(如 /user-profile)
- 动态参数:camelCase(如 :userId)
通过统一规范和工具约束,可显著降低维护成本,尤其适合中后台系统或大型项目。
5. 终极结论(建议背下来)
前端 Vue3 / JS:小驼峰 camelCase
userIduserNamecreateTime
后端 C#:大驼峰 PascalCase
UserIdUserNameCreateTime
数据库:小写 + 下划线
user_iduser_namecreate_time
重点:它们本来就不需要一致!这是规范,不是错误!
为什么必须不一样?(行业标准)
- C# 官方规范:属性必须大驼峰
C#
public int UserId { get; set; } //强制
public string UserName { get; set; } //强制
public DateTime CreateTime { get; set; } //强制- JS/TS/Vue 官方规范:变量必须小驼峰
js
const userId = 1 //强制
const userName = '张三' //强制
const createTime = '2025-06-01' //强制- 数据库规范:必须小写 + 下划线
sql
user_id
user_name
create_time这三者是三个不同的语言,各遵守各的规范!
就像:
- 中文说 “你好”
- 英文说 “hello”
- 日文说 “こんにちは”
意思一样,写法不同,完全正常!
NOTE
那怎么通信?谁来转换? ASP.NET Core 会自动帮你转! 后端 C# 大驼峰 → 传给前端时 自动变成小驼峰
后端 C#:
C#
public class User
{
public int UserId { get; set; }// 大驼峰
public string UserName { get; set; }
}前端收到的 JSON:
json
{
"userId": 1,// 小驼峰
"userName": "张三"
}前端使用:
js
formData.userId = 1
formData.userName = "张三"完整自动流转过程(一看就懂)
- 数据库(下划线)
user_id → user_name → create_time- FreeSql 自动转 → C# 大驼峰
UserId → UserName → CreateTime- .NET API 自动转 → JS 小驼峰
userId → userName → createTime- 前端 Vue 只用小驼峰
formData.userId
formData.userName
formData.createTime你最担心的问题:“前端和 C# 字段不一样,会不会对接失败?”
绝对不会!这是系统自动处理的!
- 后端返回:UserId → 前端收到:userId
- 前端提交:userId → 后端收到:UserId
全程自动,不需要你改任何代码!
你只需要记住这三句话
- Vue / JS 永远写小驼峰:userId
- C# 永远写大驼峰:UserId
- 数据库永远写下划线:user_id
最终给你的 “表单写法”(完全规范) Vue3 前端:
js
const formData = reactive({
userId: '',
userName: '',
createTime: ''
})C# 实体:
C#
public int UserId { get; set; }
public string UserName { get; set; }
public DateTime CreateTime { get; set; }数据库:
sql
user_id, user_name, create_time一句话总结:
- 前端小驼峰,后端大驼峰,数据库下划线,三者自动转换,互不影响!


