我见过最离谱的面试题是什么?是让候选人在白板上画出RESTful API的六种约束,然后问他PUT和PATCH的区别。这类问题就像在问"你用过Java的四大天王吗"——听起来很专业,实际上屁用没有。
我们都在意淫REST
先说个暴论:90%的国内工程师根本不懂REST。他们只是把HTTP方法当成了万能钥匙,GET就是查,POST就是增,PUT就是改,DELETE就是删。然后美其名曰"RESTful API"。
Roy Fielding老爷子当年提出REST的时候,人家说的是"表征性状态转移",重点是"无状态"、"分层系统"、"统一接口"。结果到了国内,全TM变成了"用HTTP动词操作资源"。
你见过一个接口长这样吗?
GET /api/getUser?id=123
POST /api/addUser
POST /api/updateUser
POST /api/deleteUser
这不是REST,这是穿了个HTTP马甲的RPC。这种代码我见过至少一百个项目的里。
真实案例:那个凌晨三点的线上事故
2023年(编的年份,别在意),某电商平台大促,接口超时,用户下单失败。排查了一圈,发现问题出在一个"看似很RESTful"的接口上:
GET /api/orders/{orderId}/items/{itemId}
这个接口查订单商品,N+1查询,每个订单十几件商品,QPS一上来数据库直接爆炸。
问题在哪?问题不在于REST,而在于这个人把REST当成了银弹。他觉得只要URL设计得"优美",性能问题就会自动消失。这不是架构设计,这是意淫。
后端真正的困境:业务复杂度和接口泛滥
真实的业务后端面临的是什么问题?是业务逻辑本身,不是URL够不够"REST"。
举个例子,库存扣减。简单吗?简单。一个SQL:
UPDATE inventory SET stock = stock - ? WHERE product_id = ? AND stock >= ?
但是,你要在高并发下保证不超卖、不少卖、不死锁。光是库存就够你写三篇论文的。更别说还有价格计算、优惠券、满减活动、会员折扣这些业务逻辑搅在一起。
你告诉我,这种业务怎么用RESTful?GET /inventory/decrease?productId=xxx&quantity=1 ?
GraphQL也救不了你
有人说了,REST不行,你试试GraphQL。好的,我们来聊聊GraphQL。
GraphQL解决的是什么问题?是"一次拿完我所需要的数据"。听起来很美是吧?实际呢?
第一个坑:N+1问题。你以为前端会乖乖写好字段路径?不存在的。前端会写个 * 然后所有字段都查,数据库压力翻倍。
第二个坑:缓存。你试试给GraphQL做HTTP缓存?做不了,因为GET请求里带body,而HTTP规范里GET是不带body的。你自己实现缓存?好的,欢迎掉进这个大坑。
第三个坑:监控。你知道哪个字段被查了多少次吗?你知道哪个嵌套查询拖慢了整体响应吗?你不知道,你只能靠日志慢慢猜。
GraphQL不是银弹,它只是把问题从"接口碎片化"变成了"查询碎片化"。烂的架构,用什么协议都是烂的。
我的建议:实用主义API设计
说了这么多,不是让你们不用API设计规范,而是要明白:规范是为人服务的,不是人为规范服务的。
第一,不要为了REST而REST。 如果你的业务就是简单的CRUD,用什么无所谓,整洁就行。如果你的业务是高度定制化的,不要硬套REST,该用BFF就用BFF,该用GraphQL就用GraphQL,该用gRPC就gRPC。
第二,URL设计的第一原则是可读,不是"正确"。 /api/getUser 和 /api/users/123 哪个更好?不是后者,是看场景。前者更明确,后者更规范。你要纠结的是你们团队怎么保持一致,而不是哪个更符合Fielding的论文。
第三,版本管理比设计模式重要。 你可能不知道V1版本接口有多少在跑着上古代码。提前想好版本策略,该废弃的接口要敢于下掉,别搞成"祖传接口"。
第四,文档和协议同样重要。 你设计得再漂亮,别人不会用等于零。OpenAPI/Swagger该写就写,接口文档该更新就更新。这不是形式主义,这是工程协作的基本素养。
写在最后
技术选型这东西,最怕的不是选错,是选了一个看起来很对但实际不适合的东西,然后花大量时间去证明"它是对的"。
RESTful也好,GraphQL也好,gRPC也好,都只是工具。工具的价值在于解决问题,而不是证明你读过某篇论文。
下次有人问你"你这个接口REST吗",你可以直接反问他:"你的业务复杂还是Fielding的论文复杂?"
如果他答不上来,那他可能连自己在设计什么都不知道。
(本文不构成任何技术建议,你爱怎么写怎么写,写bug了别找我)