资讯详情

RESTful接口设计核心:资源、路径与HTTP方法的规范化指南

📅 2026/10/22 18:18:42 | 华诺云谱 👁 阅读
RESTful接口设计核心:资源、路径与HTTP方法的规范化指南
做后端开发这些年我见过太多接口设计翻车的现场。明明就是一个用户模块愣是有人写出/getUser、/user/delete、/deleteUserById这种路径参数一会儿塞在路径里一会儿放在 query 里方法也是 GET 和 POST 随缘用。问起来各有各的理由能跑就行、前端要什么我就给什么、老接口动不得。可真等接口量上了两位数每个人都在骂自己当初挖的坑。RESTful 风格能沉淀这么多年我觉得核心就一句话把资源、路径、方法三者之间那层本就很自然的对应关系用一套约定俗成的规则固化下来。资源是名词路径是资源的门牌号方法是我们要对资源执行的动作。搞明白这三者的关系接口设计不说直接起飞至少不再是“能跑就行”的野路子。这篇内容适合刚接触后端接口设计的新人也适合想梳理现有接口规范、准备做接口整改的同学。1. 接口设计的病根动词满天飞名词无处安放1.1 为什么大家一开始都会写出“动词路径”我刚接触接口设计时第一直觉也是“这个接口是干嘛的就写成什么”。比如“获取用户信息”那就写get_user_info“删除订单”那就写order_delete。这种思路特别容易理解但问题也特别明显路径里全是动词接口的行为逻辑全靠“翻译”路径来理解而 HTTP 协议本身最底层的 GET、POST、PUT、DELETE 反而成了摆设。仔细想想这种做法的本质是把接口当函数调用。函数名里包含动作是很正常的但 URL 不是函数名URL 是“东西”的地址。好比去快递站取件你会说“去三号货架的包裹区”而不是“去执行取快递的动作”。RESTful 的思路就是把“东西”和“对东西做的事”分开URL 只描述东西做什么动作由方法来说。这一个认知转变基本决定了接口设计是长久的还是临时应急的。1.2 资源、路径、方法三者的分工模型这里用一个很朴素的类比把整个系统比作一个实体仓库。资源仓库里的货品分类比如“用户”“订单”“商品”。它们是名词代表一类真实存在的东西。路径仓库里的货架编号比如/users、/users/123告诉所有人“这类东西放在哪儿”“具体哪一个在哪儿”。方法你对货架上货品能做的操作。拿走查询GET、放新的POST、整箱更换PUT、调整几个字段PATCH、报废丢弃DELETE。三者不是割裂的而是一套搭配路径锁定资源方法表达动作。同一个路径配上不同的方法就代表了这一个资源的不同操作。这也是 RESTful 最有价值的地方——通过一套固定的“名词 动词”组合就能表达出几乎全部的业务动作。从工程角度看这种分工还有一个很实际的好处路径收敛之后缓存规则、权限控制、日志统计、错误处理都变得统一。你可以在网关层直接写规则“所有 GET 请求走缓存所有 POST/PUT/PATCH/DELETE 走鉴权”而不需要针对每个接口单独配置。也就是说规范化的价值不是审美是实打实的维护成本下降。2. 资源一切接口设计的实体基础2.1 如何从业务里识别“资源”资源不是靠拍脑袋定的它通常对应业务中的核心数据对象用户、订单、商品、文章、评论、发票……一个最简单也最实用的判断标准如果这个东西可以在业务里被独立创建、查询、修改或删除并且有唯一标识比如 ID那它就是一个资源。举个反例订单里的“订单状态”是一个字段不是独立资源。虽然你也会对它操作比如把待支付改成已取消但它没有独立的生命周期不能脱离订单存在。这时候正确的做法不是设计一个/order-statuses资源而是把状态变更落到订单本身上PATCH /orders/{id}只传{ status: cancelled }或者设计一个表达动作的接口POST /orders/{id}/cancel。可能有人会问那“支付”算不算资源严格说支付可以有自己的流水记录有自己的 ID有独立的创建和查询动作所以把它看成一个资源也完全合理对应/payments。如果你们的支付流水需要独立查询、对账那就值得拆出来如果只是订单上的一个状态字段那就没必要拆分。资源粒度本身就是业务边界的外在表现。2.2 资源命名的常见陷阱资源命名是接口设计里第一个容易内耗的地方。我总结了三种最常见的踩坑姿势。第一路径里出现动词。比如/getUser、/createOrder。这种写法的直接后果是一个资源配上增删改查四种动作就要写四个带不同动词的路径路径数量直接膨胀而且动作一多比如“审核”“发布”“关闭”路径就会变成一本词典。正确的做法是把动词交给 HTTP 方法路径里只保留名词/users、/orders。第二业务化命名替代通用命名。比如把“用户”写成/userInfo、把“订单”写成/orderDetail。这在业务上好像更“明确”但很容易造成一个资源多个名字用户既有/userInfo又有/profile前端同学每次都来问到底该调哪个。资源路径是资源的唯一名字一个资源有且仅有一个典型路径。第三单复数混用。有的接口用/user有的用/users有的用/userList。我建议统一使用复数形式/users、/orders、/products。原因有两个一是/users天然表达“集合”的语义GET /users是获取用户列表GET /users/123是获取某个用户语义非常顺二是绝大多数团队都沿用复数约束外部工具、代码生成器、API 网关的默认规则也普遍偏好复数跟随主流通用成本最低。关于子资源当两个资源存在清晰的从属关系时路径层级可以体现这种关系/users/123/orders表示“某个用户下的订单列表”。这一点我在下一章再展开说。3. 路径资源地址的书写规则3.1 路径层级与命名规范路径设计的核心原则是能少一层就少一层能简单就别花哨。我整理了几条在团队里定死就会少很多争论的规则。一律小写。URL 对大小写敏感/Users/123和/users/123会被当成两个东西这种坑一旦上线排查起来极其难受。单词间用连字符不用下划线。/user-profiles比/user_profiles在绝大多数系统和浏览器里更通用也避免了下划线被某些终端误处理的问题。不用文件扩展名。/users.json、/orders.html这类设计是早期把 URL 当文件路径的遗留思维RESTful 下资源是抽象概念不是物理文件。用 ID 定位具体资源/users/123而不是/users/show?id123。ID 是路径中的一部分这是“资源有唯一地址”的直接体现。路径层级一般不超过三层。超过三层大概率是设计出了问题这一点在 6.3 里我会展开讲。还有一个细节路径里的 ID 用数字还是字符串没有统一答案但建议全局一致。有的接口用数字自增 ID有的用 UUID 或业务单号。我见过团队把订单号从字符串改成数字后历史路径全部作废前端缓存、分享链接、书签全部失效。所以 ID 类型这个决策要在项目早期就定死。3.2 路径与查询参数的分工这是很多人的知识盲区到底哪些信息该放在路径里哪些该放在 query 也就是?后面的查询参数里。判断标准其实一句话路径里的动态段是用于定位资源的标识query 参数是用于对资源集合进行筛选、排序、分页的附加条件。举个例子GET /orders?statuspaidpage2size10这个接口表示“获取订单列表筛选已支付状态取第 2 页每页 10 条”。status、page、size都不是资源标识而是对订单集合的过滤条件放在 query 里是最自然的。再看一个反面设计GET /orders/status/paid/page/2这个路径读起来就像“支付状态下面有一个叫第 2 页的资源”但“第 2 页”不是资源“支付状态”也不是资源。这种设计每多一个筛选条件路径就要多一级最后变成一长串没法读的字符串维护的人看一眼就想离职。另外资源域名空间不要放操作型参数。比如POST /orders/{id}/status这种写法看着像是给订单设置状态但status并不是独立资源它只是订单的属性字段。更稳妥的写法是PATCH /orders/{id}请求体里带{ status: paid }。当然如果你已经把“状态”抽象成独立资源那是另一回事路径上出现它才合理。核心还是那两个字资源。4. HTTP 方法把“动词”还给协议4.1 五个标准动词的语义与选择HTTP 协议本身就定义了丰富的动词RESTful 只是把它们的语义和资源操作对齐了。下面是沿用至今的主流对应关系方法语义典型路径是否幂等说明GET查询资源或资源列表GET /users/123是不改变资源状态POST创建新资源或触发一个业务动作POST /users否每次请求都会产生新资源PUT整体更新用请求体整体替换资源PUT /users/123是完整覆盖PATCH局部更新只修改指定字段PATCH /users/123否不保证部分修改DELETE删除资源DELETE /users/123是删除后再次删除通常返回错误但资源状态已变很多初学者看这个表会觉得简单实际落地时最容易困惑的是 PUT 和 PATCH 的差别。我讲一个屡试不爽的判断方法这次更新能不能用“原地替换”来描述。比如修改用户全部资料前端把整个表单都传过来后端整体覆盖用 PUT如果只是改一个手机号、一个备注或者某些字段可能为空且不能影响其他字段那就用 PATCH。很多人踩过最大的坑是用 PUT 做局部更新把没传的字段全置空了这个记忆点非常痛。4.2 方法选型中最容易犯的错误第一个错误用 GET 做删除。GET /orders/123/delete这种设计很危险因为 GET 请求在浏览器里可以被预取、被缓存、被爬虫爬到。你永远不知道什么时候一个搜索引擎的爬虫就把你的订单删了。删除操作必须用 DELETE 方法实在服务端框架不支持 DELETE 时这种老框架确实还有也至少用 POST不要用 GET。第二个错误用 POST 做所有写入操作包括更新和删除。很多人只认两个方法GET 和 POST。结果就是接口路径里出现/orders/update、/orders/delete绕了一圈又回到动词路径的老路。POST 确实是最自由的方法但正因为自由API 的语义就变得模糊前端拿到接口文档根本无从判断一个接口是否幂等、是否可以安全重试。第三个错误DELETE 带请求体。部分框架支持在 DELETE 里塞 JSON body但很多代理层、网关、浏览器环境会直接丢弃请求体导致删除参数丢失。最稳妥的做法是需要删除什么资源就把它标识放到路径上DELETE /orders/123或 query 参数里DELETE /orders?order_id123不要依赖 DELETE 的 body。我个人的经验是方法选择本身也承载着接口契约。前端看到一个PATCH /users/123就能立刻明白这是“改用户”且不会影响其他字段看到一个POST /users就明白这是“新增用户”不能盲目重试。这些约定不需要读文档只看方法加路径就八九不离十这就是 RESTful 的设计红利。5. 完整实例从零设计一张订单接口映射表5.1 真实需求场景与资源拆分光讲概念容易飘我拿一个虚构电商系统的订单模块来展示完整设计过程。假设需求是这样用户能创建订单用户能查看订单详情和订单列表列表支持按状态筛选、分页用户能修改订单备注用户能取消订单、对订单发起支付管理端能删除异常订单第一步识别资源。这里的核心资源是订单orders。订单下的商品项从属于订单不需要暴露独立接口所以只涉及两层资源/orders和单个订单/orders/{order_id}。第二步处理“取消”“支付”这类动作。这两个动作看起来不是标准的增删改查怎么办有两种主流方案。方案一把动作解释为订单资源的状态变更用 PATCH 修改status字段。方案二设计动作子路径比如POST /orders/{order_id}/cancel和POST /orders/{order_id}/pay语义直白。我更推荐方案二原因很实际支付流程会涉及支付渠道回调、支付流水、幂等票据等一堆附加数据把这些都塞进一个 PATCH 的 body 里会非常臃肿而取消订单往往有复杂的业务校验未支付才能取消、库存要释放。这种强业务动作单独用一个子路径表达逻辑清晰、也方便在网关做特殊超时和鉴权配置。5.2 完整接口映射表与返回约定基于上面的分析我给出这组订单模块的接口映射接口语义方法路径请求要点订单列表GET/ordersquerystatus、page、size、sort创建订单POST/ordersbody商品项、地址、备注订单详情GET/orders/{order_id}无 body修改备注PATCH/orders/{order_id}body 只传备注字段支付订单POST/orders/{order_id}/pay可选 body支付方式取消订单POST/orders/{order_id}/cancel可选 body取消原因删除订单DELETE/orders/{order_id}管理端操作以“修改备注”为例请求是这样的PATCH /orders/o202503011234 Content-Type: application/json { remark: 客户说尽量晚一点发货 }后端只更新remark字段其他字段不受影响。如果要实现“更新整个订单的快照信息”才用 PUT 整体覆盖。把 PATCH 和 PUT 的边界在需求评审时就定好能省掉后面大量的沟通成本。列表查询的分页我建议统一用page和size两个参数响应里返回总数和当前页数据而不是把分页信息塞进路径。这样前端可以只通过 query 变化就完成列表筛选浏览器回退按钮都能正常工作。5.3 反模式自查清单设计完正向的表再看一组反模式。下面这些写法在真实项目中随处可见每一条都对应一个明确的修正方案反模式路径问题正确写法GET /getUserById?userId123动词路径 ID 放 queryGET /users/123POST /updateUser用服务器动作命名路径PUT /users/123 或 PATCH /users/123DELETE /deleteOrder?orderId1删除动词路径DELETE /orders/1GET /orderListByUser?userId1从属关系表达混乱GET /users/1/ordersPOST /getOrders查询却用 POST丧失缓存能力GET /ordersPATCH /users/123/status?valuepaid把属性当子资源PATCH /users/123body 带 status做接口评审的时候直接把这张表贴在文档里让大家对着自查比讲十遍理论都管用。6. 常见问题与排查技巧实录6.1 状态码 404、400、405 到底该返回哪个接口报错时状态码选不对很影响前端排查效率。我见过一个团队把“订单不存在”返回 400前端拿到 400 以为是参数格式错了查了半天才发现是数据不存在极其浪费时间。我的建议非常简单400 Bad Request请求本身有语法问题参数缺失、格式错误、body 不是合法 JSON一律返回 400。404 Not Found资源不存在。包括路径不存在、资源 ID 查不到记录。从 RESTful 视角看GET /orders/999999返回 404 是准确的因为名为 999999 的订单资源确实不存在。405 Method Not Allowed资源存在但当前方法不被支持。比如接口只有 GET却来了个 DELETE就返回 405。还有一个容易被忽略的点校验参数时到底是返回 400 还是 422。如果你的项目里已经有字段级校验错误的结构用 422 表达“请求格式合法但业务校验不通过”会更细腻。不是所有团队都必须上 422但至少要在内部定一套统一规则别今天 400 明天 500。6.2 POST、PUT、PATCH 的选择困境每次评审接口都有人在 POST、PUT、PATCH 之间纠结。我提供一个决策流程照着走基本不会错这个操作会让系统里多出一个新的资源实例吗如果是用 POST。这个操作是把现有资源整体替换吗如果是用 PUT。这个操作只是改资源的部分字段吗如果是用 PATCH。这个操作是执行一个业务动作比如支付、取消、审核既不是纯增删改查中的任何一个用 POST路径上表达动作。有个特殊情况如果项目处在早期字段少、更新一个资源时前端总是传全量字段有人会嫌 PATCH 麻烦而全用 PUT。这个取舍短期能接受但一旦字段超过十个PUT 的“整体替换”语义就会变成深坑——比如两个客户端同时更新后提交的那个把先提交的字段全覆盖了。所以我建议从第一天就区分 PUT 和 PATCH别等出事再改。6.3 路径嵌套层级太深怎么办有的业务天然有层级订单里有商品项商品项里又关联库存变动库存变动又要关联单据。如果无脑嵌套就会写出这样的路径GET /orders/{order_id}/items/{item_id}/stock-changes/{change_id}这个路径已经恶心到没法读了。我的处理经验是优先减少不必要的层级。如果stock-changes本身有独立查询需求比如财务对账就把它提升为顶层资源GET /stock-changes?order_idxxitem_idxx。从属关系是否用层级表达取决于“子资源是否离开父资源就没有存在意义”。订单商品项离开订单确实没意义所以/orders/{order_id}/items合理而库存变动离开订单也有自己的生命周期就不适合硬嵌在订单路径下。路径层级超过三层时停下来重新审视资源建模而不是继续往下接。记住路径不是用来描述完整对象图的它是给资源定位用的。对象之间的关系可以在查询参数里体现不一定非要进路径。6.4 团队协作时如何落地这套规范规范写起来容易让团队每个人都遵守就难了。我在几个团队推过接口规范效果最好的套路是下面这几个第一用 API 文档工具把规范固化成模板。在项目里统一用 OpenAPI 描述接口把路径、方法、参数、响应结构全部固化下来。新人照着模板写想写出动词路径都难。代码生成器甚至可以直接从 OpenAPI 生成客户端和服务端代码减少手写接口的随意性。第二代码评审时把“资源路径 方法”作为一个单独检查项。只要路径里出现动词、方法选错、ID 放在 query 里评审直接打回。坚持两三个迭代团队就会形成肌肉记忆。第三定期做接口审计。每隔一两个月从网关或者日志里拉一次现有接口清单人为review一遍有没有路径重复、有没有方法乱用、有没有一个资源多个路径。发现问题列一个整改清单灰度切换就好。老接口可以慢慢改但新的必须守规矩。我给团队讲接口设计时经常说一句话接口规范不是限制你是在帮你把每天都在做的最无聊的决策自动化。路径怎么写、方法怎么选本来就应该是一套肌肉记忆不值得每次上线前集体纠结。最后分享一个我这几年最大的体会接口规范这件事技术上一点都不难难的是让团队认识到“路径和方法选错了不是在写代码是在挖坑”。每次后端同学写出/queryOrderList这种路径时我都建议他停下来问自己一句这个接口操作的资源是什么它叫什么名字我对它做了什么动作这三个问题答清楚接口设计就成功了七成。规范最大的价值就是让每个接口在被人看到的第一个瞬间就已经把话说清楚了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑