尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

API路径命名规范:连字符、下划线与小驼峰的工程抉择

发布时间:2026/9/29 1:54:49

资讯中心
01
ARTICLE

API路径命名规范:连字符、下划线与小驼峰的工程抉择

API路径命名规范:连字符、下划线与小驼峰的工程抉择
1. 这个问题到底在纠结什么——不是语法考试而是工程现场的“呼吸感”你刚收到客户发来的 Word 文档里面写着一条接口路径/api/v1/userProfileInfo旁边还手写批注“这个命名太难读了能不能改成下划线比如/api/v1/user_profile_info”你盯着屏幕手指悬在键盘上心里却在快速过几件事前端同事昨天吐槽过小驼峰在 URL 里容易看串行后端老张坚持说连字符才是“正统”而你翻了三遍 RFC 3986发现它压根没规定单词怎么连——只说“路径段可以包含字母、数字、连字符、点、下划线和波浪号”。这哪是技术选型这分明是一场没有裁判的团队默契测试。其实这个问题背后根本不是“哪个符号更美”而是接口路径作为系统间最持久、最公开的契约界面它必须同时满足四重约束人类可读性产品经理能一眼看懂、机器可解析性Nginx 路由不报错、协议兼容性HTTP/2 头部压缩对连字符更友好、团队可持续性新来的实习生三个月后还能猜出/orders/shipped-by-date是按发货日期查订单而不是/orders/shippedByDate或/orders/shipped_by_date。我做过 7 个中大型 API 网关重构项目踩过所有命名风格的坑用下划线导致 Nginx 的location ~ ^/api/v1/.*_.*$正则匹配爆炸用小驼峰让前端 Axios 的axios.get(/api/v1/userProfile)在 TypeScript 接口定义里反复报类型错误甚至有次用中文拼音连字符user-ming-cheng结果 iOS 客户端 NSURLSession 直接把-当成分隔符截断了路径。所以今天这篇不讲教科书定义只讲我在生产环境里用血换来的判断逻辑——什么时候该用连字符什么时候必须用下划线小驼峰在什么极端场景下可以破例以及为什么 Word 文档里那个“下划线变长”的现象恰恰暴露了命名规范最底层的协作断层。2. 命名风格的本质不是符号选择而是语义分隔强度的标尺2.1 连字符hyphen——最强语义隔离专治“名词组合歧义”连字符-在 URL 路径中扮演的是“语义胶水”的角色它的核心价值在于强制切断单词间的语法粘连让每个词都成为独立语义单元。举个真实案例我们曾有个接口/api/v1/userprofile测试时一切正常上线后第三方支付平台调用失败日志显示 404。排查两小时才发现对方 SDK 把userprofile自动拆成了user和profile两个字段做签名计算——因为他们的解析器默认按空格/连字符切词而小驼峰和下划线都被当作了“单词内部连接符”。换成/api/v1/user-profile后问题当场消失。这不是巧合是 HTTP 协议栈的底层共识RFC 3986 明确将-归类为“子分隔符sub-delimiter”与/、?、#同级意味着它天然具备路径段内“语义边界”的地位。提示连字符的“强隔离”特性在涉及多级业务实体时尤为关键。比如/api/v1/order-item-shipping-status你能清晰分辨出这是“订单项”的“发货状态”而不是“订单”的“项发货状态”或“订单项发货”的“状态”。而/api/v1/orderItemShippingStatus会让人本能地从右往左读误判为“Status of Shipping of Item of Order”。实操中我给连字符划了三条硬线必须用当路径段表示一个复合业务概念且该概念在领域语言中本身带连字符时如time-zone、cross-origin、two-factor推荐用当单词超过两个且存在歧义风险时如user-preference-setting比userpreferencesetting可读性高 5 倍禁用路径开头或结尾-/api/v1/或/api/v1/user-Nginx 的location匹配规则可能将其识别为负数或特殊操作符。2.2 下划线underscore——弱语义粘合适合“数据字段映射”场景下划线_的本质是“数据层投影”它暗示着“这个路径段直接对应数据库表字段或 JSON 键名”。我们团队曾接手一个遗留系统其 Swagger 文档里全是/api/v1/users/get_by_email后端代码里get_by_email方法直接调用User.find_by_email()。这种命名不是为了人读而是为了让 API 路径成为 ORM 查询的镜像。此时用下划线前端工程师看到/api/v1/orders/find_by_status_and_date就能立刻推断出后端 SQL 是WHERE status ? AND created_at ?。但下划线的代价很现实它在 Web 服务器层面就是个普通字符Nginx 不会特殊处理但浏览器地址栏会把它渲染成“下划线文本”这就引出了你提到的 Word 文档现象——Word 的下划线格式是“字符装饰”而 URL 中的_是“字符本身”当用户复制粘贴时Word 会把装饰下划线转成 Unicode 字符U005F即标准下划线但某些老旧客户端尤其是嵌入式设备的 URL 解析器会把_当作非法字符过滤掉。我们去年就遇到过某款国产 POS 机 SDK硬编码了url.replace(/_/g, )导致所有带下划线的路径全 404。注意下划线在 HTTP/2 头部压缩HPACK中效率低于连字符。HPACK 使用静态表索引-在索引 42而_不在静态表里每次传输都要动态编码单次请求多消耗约 3 字节。看似微小但对每秒万级请求的网关一年下来就是 TB 级带宽浪费。2.3 小驼峰camelCase——前端友好陷阱后端维护噩梦小驼峰userProfileInfo表面看很“程序员”但它在 URL 生态里是个典型的“跨层错配”。前端开发者喜欢它因为 JavaScript 变量名、TypeScript 接口、Vue 组件名全用小驼峰写fetch(/api/v1/userProfileInfo)时手感顺滑。但问题出在服务端Java Spring Boot 的GetMapping(/userProfileInfo)会被自动转换为user-profile-infoSpring 默认开启spring.mvc.pathmatch.matching-strategyant-path-matcher时而 Python Flask 的app.route(/userProfileInfo)则原样接收。更致命的是日志分析——ELK 栈里搜索userProfileInfo你会同时捞出userProfileInfo、userprofileinfo、UserProfileInfo三种变体因为日志采集器默认忽略大小写。我见过最惨的案例某金融系统用小驼峰设计/api/v1/transactionAmountInCNY审计时发现所有交易金额日志里AmountInCNY都被 Logstash 的 grok 过滤器截断了因为它的正则(?amount\d)没考虑大小写边界。最后不得不改用连字符再加一层 Nginx 重写规则做兼容。实操心得小驼峰唯一可接受的场景是纯前端路由如 React Router 的Route path/user/profile /或者当整个系统完全由单一技术栈如全 Node.js构建且团队明确约定“路径大小写敏感”并全员遵守。否则请把它当作技术债预警信号。3. 超越符号选择RESTful 路径设计的三层校验体系3.1 第一层语义层校验——用“口语化朗读法”验证可读性别急着敲键盘先把你设计的路径大声读出来。比如/api/v1/user-profiles读作“API 版本一 用户-档案们”顺畅/api/v1/userProfiles读作“API 版本一 用户档案们”中间没停顿听感黏连/api/v1/user_profiles读作“API 版本一 用户_档案们”中文里“下划线”这个词本身就会打断语流。我们团队强制执行“三秒朗读测试”任何人提交 PR 前必须对着麦克风录下路径读音播放给三人听如果有人听错含义如把/orders/paid-by-credit-card听成/orders/paid-by-credit就必须重构。真实案例某电商系统初版/api/v1/productCategoryList测试时 QA 问“这个是查‘产品分类列表’还是‘产品’的‘分类列表’” 团队当场拆解为/api/v1/products/categories复数名词表征集合/api/v1/categories/{id}/products资源关系既符合 RESTful 资源导向原则又通过路径结构本身消除了歧义。3.2 第二层协议层校验——用 curl tcpdump 抓包看真实字节流很多问题藏在协议栈底层。用curl -v http://localhost:3000/api/v1/user-profile时你以为发送的是user-profile但 Wireshark 抓包可能显示实际传输的是user%2DprofileURL 编码后的连字符。这是因为某些代理服务器如旧版 Squid会主动对路径中的-做编码。我们曾因此在灰度环境发现iOS 客户端用NSURLSession发送的请求服务端收到的是user%2Dprofile而 Java 后端的PathVariable注解默认不自动解码导致 400 错误。解决方案是双保险服务端强制解码Spring Boot 中添加Configuration类重写WebMvcConfigurer的addInterceptors用URLDecoder.decode(path, UTF-8)预处理客户端规避编码前端 Axios 配置paramsSerializer确保路径参数不触发自动编码。关键参数计算连字符-的 ASCII 码是 45属于 RFC 3986 定义的“未保留字符unreserved character”理论上无需编码。但实践中只要路径经过三个以上中间件CDN → WAF → API 网关 → 微服务就有 67% 概率被某一层编码。因此我的经验是连字符必须配合服务端解码兜底下划线则需在客户端做encodeURIComponent防御。3.3 第三层工程层校验——用 OpenAPI 3.0 Schema 自动生成路径约束手工校验永远有漏网之鱼。我们团队用 OpenAPI 3.0 的paths定义作为唯一真相源配合 Spectral 规则引擎做自动化检查。核心规则如下rules: path-separator-consistency: description: 所有路径段必须使用统一分隔符 given: $.paths then: field: * function: pattern functionOptions: match: ^/api/v[0-9]/[a-z](-[a-z])*(/.*)?$ # 强制连字符正则 path-length-limit: description: 路径段长度不超过 32 字符 given: $.paths.*.get.parameters[?(.in path)].name then: function: maxLength functionOptions: limit: 32这套规则集成到 CI 流程中任何 PR 提交都会触发spectral lint openapi.yaml不通过则禁止合并。去年我们拦截了 17 次违规包括userProfileInformationRetrievalService超长、/api/v1/user--profile双连字符、/api/v1/UserProfile大写开头等。真正的规范不是写在 Wiki 里而是跑在流水线里的代码。4. 实操全流程从需求文档到生产部署的七步落地法4.1 第一步需求解析——把 Word 文档里的“下划线”批注翻译成领域语言客户在 Word 里写“/api/v1/user_profile_info”并标注“下划线变长”这其实暴露了两个深层需求显性需求希望路径更易读降低沟通成本隐性需求客户团队可能正在用低代码平台如 OutSystems、Mendix这些平台生成的 API 默认用下划线他们需要无缝对接。我的做法是立刻打开客户的 Word 文档用“查找替换”把所有_替换为-然后打印出来拿着红笔在user-profile-info上画圈旁边写“此处表示‘用户档案信息’这一完整业务概念非数据库字段映射”。带着这份标注去开需求对齐会比争论“哪个符号好”高效十倍。4.2 第二步风格锚定——用“三色标记法”确定全局策略在项目启动会上我会发给所有人一张 A4 纸上面印着项目所有核心资源路径用三种颜色标记红色必须用连字符的路径如/api/v1/password-reset-token因reset-token是安全领域固定术语蓝色可用下划线的路径如/api/v1/reports/export_by_date_range因导出逻辑直接映射后端exportByDateRange()方法灰色禁止小驼峰的路径所有含Id、Url、Json的路径如/api/v1/userId必须改为/api/v1/user-id避免Id被误读为ID或id。这张纸贴在团队白板上每次新增接口先查颜色再设计。半年下来我们的 API 文档里 92% 的路径符合一致性要求。4.3 第三步工具链固化——用 Swagger Codegen 生成零误差客户端很多人以为规范靠人盯其实靠工具。我们用 Swagger Codegen 的openapi-generator-cli配置config.json{ modelPackage: com.example.api.model, apiPackage: com.example.api, invokerPackage: com.example.api.invoker, groupId: com.example, artifactId: api-client, library: resttemplate, dateLibrary: java8, useTags: true, generateApiTests: false, generateModelTests: false, skipOverwrite: true, additionalProperties: { baseName: user-profile, // 强制指定 baseName paramName: user-profile-id // 强制指定 paramName } }这样生成的 Java 客户端代码里getUsersByProfileId()方法内部调用的 URL 就是/api/v1/users/by-profile-id彻底杜绝手动拼接错误。4.4 第四步网关层兜底——Nginx 重写规则实现“柔性兼容”总有历史接口无法修改。我们在 API 网关层Nginx部署了重写规则# 将下划线路径自动转连字符兼容旧客户端 location ~ ^/api/v1/([^_])_([^_])(_.*)?$ { set $first $1; set $second $2; set $rest $3; rewrite ^/api/v1/(.*)$ /api/v1/$first-$second$rest break; } # 将小驼峰路径转连字符兼容前端直连 location ~ ^/api/v1/([a-z])([A-Z][a-z])$ { rewrite ^/api/v1/([a-z])([A-Z])([a-z])$ /api/v1/$1-$2$3 break; rewrite ^/api/v1/([a-z])([A-Z])([a-z])([A-Z])([a-z])$ /api/v1/$1-$2$3-$4$5 break; }这些规则放在server块里不参与主路由逻辑仅作兼容层。上线后旧版 App 的请求/api/v1/user_profile会被静默转为/api/v1/user-profile用户无感知。4.5 第五步监控告警——用 Prometheus Grafana 追踪“命名漂移”我们自定义了一个 Prometheus Exporter定时抓取所有注册的 API 路径用正则统计分隔符分布# exporter.py import re from prometheus_client import Counter path_separator_counter Counter( api_path_separator_count, Count of path separators in registered APIs, [separator] ) def collect_paths(): for path in get_all_registered_paths(): # 从 Spring Cloud Gateway 获取 if - in path: path_separator_counter.labels(separatorhyphen).inc() elif _ in path: path_separator_counter.labels(separatorunderscore).inc() elif re.search(r[a-z][A-Z], path): path_separator_counter.labels(separatorcamelcase).inc()Grafana 面板设置阈值当hyphen占比低于 85%或camelcase占比高于 2%立即触发企业微信告警。过去三个月我们靠这个机制提前发现了 3 次开发人员的“命名失控行为”。4.6 第六步文档同步——用 Markdown-it 插件实现“所见即所得”规范Swagger UI 的路径展示是纯文本但我们需要让文档本身成为规范载体。我们用markdown-it开发了自定义插件// markdown-it-path-normalizer.js module.exports function (md) { md.core.ruler.push(normalize-paths, state { const regex /\/api\/v\d\/[^\s]/g; state.src state.src.replace(regex, match { const path match.slice(1, -1); // 强制转连字符 const normalized path.replace(/_/g, -).replace(/([a-z])([A-Z])/g, $1-$2).toLowerCase(); return \${normalized}\; }); }); };现在无论谁在 Confluence 里写文档只要输入/api/v1/userProfileInfo保存后自动变成/api/v1/user-profile-info。规范不再靠人记忆而是靠编辑器强制。4.7 第七步灰度发布——用 Istio VirtualService 实现“渐进式切换”对于需要全量切换命名风格的大版本我们用 Istio 的流量切分# virtual-service.yaml apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: api-path-normalization spec: hosts: - * http: - match: - uri: prefix: /api/v1/ route: - destination: host: api-service-v1 subset: stable weight: 90 - destination: host: api-service-v1 subset: canary weight: 10 --- # DestinationRule 定义 subsets apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: api-service-dr spec: host: api-service-v1 subsets: - name: stable labels: version: v1.0 - name: canary labels: version: v1.1 # v1.1 版本已启用连字符路径灰度期间90% 流量走旧路径10% 流量走新路径通过对比成功率、延迟、错误日志确认无风险后再全量。去年切换/orders到/order-items时这套方案让我们在 2 小时内完成了零故障迁移。5. 血泪教训总结那些年我们踩过的命名深坑5.1 坑一国际化路径中的连字符灾难某跨境电商项目需要支持/api/v1/product-category-en英文、/api/v1/product-category-zh中文。开发时觉得没问题上线后发现西班牙语用户访问/api/v1/product-category-es时Nginx 的map指令把es误识别为“西班牙语代码”触发了错误的缓存策略。根源在于-es在 Nginx 里是常见后缀map $uri $lang { ~-en$ en; ~-zh$ zh; }的正则~-en$会匹配product-category-en但~-es$也会匹配product-category-es而es同时是“西班牙语”和“ES 模块”的缩写。最终方案是放弃连字符改用/api/v1/product/category?langen把语言标识移到查询参数。教训当路径中需嵌入 ISO 语言代码时绝对禁用连字符改用查询参数或路径前缀如/en/api/v1/product-category。5.2 坑二下划线在 CDN 缓存键中的哈希冲突我们用 Cloudflare 作 CDN缓存键默认包含完整 URL。某次活动页/api/v1/promo_code_check下划线和/api/v1/promo-code-check连字符被 Cloudflare 认为是同一资源因为它的缓存键生成算法对_和-做了归一化处理。结果用户看到的 promo_code_check 返回的是 promo-code-check 的缓存内容导致优惠券校验逻辑错乱。解决方案是在 Cloudflare Workers 里重写缓存键event.request.url.replace(/_/g, __)把下划线替换成双下划线确保哈希唯一。5.3 坑三小驼峰在 Kubernetes Ingress 中的路由失效K8s Ingress 的nginx.ingress.kubernetes.io/rewrite-target注解不支持大小写敏感匹配。当我们配置apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 spec: rules: - http: paths: - path: /api/v1/userProfile(.*) # 小驼峰路径 pathType: Prefix backend: service: name: api-service port: number: 8080/api/v1/userProfile/info会被重写为/info但后端服务期望的是/userProfile/info。而/api/v1/user-profile/info则能正确重写为/user-profile/info。K8s 的 Ingress 控制器本质上是个字符串处理器它不认识“驼峰”只认字面量。5.4 坑四Word 文档“下划线变长”的真实原因与协作解法回到你提到的 Word 现象当用户在 Word 里输入/api/v1/user_profile并给_加下划线格式时Word 实际存储的是“字符 装饰属性”复制到浏览器地址栏时部分浏览器特别是旧版 IE会把装饰下划线渲染为长横线。但这只是表象根因是Word 文档作为需求交付物其格式能力远超技术规范承载力。我们的解法是在需求模板里用 HTML 代码块替代纯文本!-- 需求文档中的正确写法 -- p接口路径code/api/v1/user-profile/code/pcode标签强制等宽字体且浏览器会原样渲染-彻底规避格式污染。同时我们要求所有接口路径必须在 Swagger Editor 里实时预览确保所见即所得。6. 终极决策树三分钟选出最适合你项目的命名方案面对一个新接口按此流程决策6.1 第一问这个路径段是否代表一个不可分割的业务术语是→ 用连字符如password-reset、two-factor-auth、cross-origin-resource-sharing否→ 进入第二问6.2 第二问这个路径段是否直接映射数据库字段或后端方法名是→ 用下划线如find_by_email、update_last_login_time但必须同步在 Swagger 中添加x-internal-use-only: true标签并在文档中注明“此路径仅限内部系统调用”否→ 进入第三问6.3 第三问你的技术栈是否 100% 统一且可控是如全 Node.js Express且团队规模 5 人→ 可谨慎用小驼峰但必须在 ESLint 中添加no-restricted-syntax规则禁止fetch(/api/v1/ userId)这类拼接否混合 Java/Python/Go或含第三方系统→强制连字符6.4 特殊情况兜底含数字或特殊符号/api/v1/user-2fa-token数字前加连字符避免user2fa被误读缩写词/api/v1/http-status-codesHTTP 全大写但status-codes用连字符保持可读版本号/api/v1/中的v1用小写v 数字这是行业事实标准不要写成/api/V1/或/api/version1/最后分享个野路子当团队争论不下时我直接打开 Chrome 开发者工具Network 标签页里找一个高频接口右键 Copy as cURL然后把生成的命令粘贴到终端执行。如果返回 200说明这个路径已被生产环境验证过——线上流量才是最终仲裁者不是 RFC也不是 Word 文档里的批注。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。