REST API设计疑问:Upsert与‘Updelete’实现及HTTP方法选型
问题背景与疑问
数据库表结构
CREATE TABLE IF NOT EXISTS some_table ( id INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL PRIMARY KEY, user_ids TEXT[] NOT NULL, field_1 TEXT NOT NULL, field_2 TEXT NOT NULL, field_3 TEXT NOT NULL, hash_id TEXT GENERATED ALWAYS AS ( MD5(field_1 || field_2 || field_3)) STORED UNIQUE NOT NULL )
特殊业务逻辑
- 插入操作:判断
md5(field_1||field_2||field_3)是否存在,存在则追加user_id到user_ids数组;不存在则插入新行。 - 删除操作:通过数据库触发器自动处理,当
user_ids数组长度为0时自动删除该行,触发器代码如下:
CREATE OR REPLACE FUNCTION delete_row() RETURNS trigger AS $$ BEGIN IF tg_op = 'UPDATE' THEN DELETE FROM some_table WHERE id = NEW.id; RETURN NULL; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER some_table_delete_row AFTER UPDATE ON some_table FOR EACH ROW WHEN (CARDINALITY(NEW.user_ids) = 0) EXECUTE PROCEDURE delete_row();
实际无传统删除接口,需移除user_ids元素至数组为空,由数据库自动删行。
疑问
- 认为PUT方法最适合实现上述Upsert逻辑,是否合理?
- 对于减少
user_ids元素的操作,用DELETE/PUT较棘手,理想情况是像PATCH那样仅修改单个字段且不允许手动删除,该如何选型? - 自动生成的hash_id虽便捷,但不确定是否适合删除场景:端点为
base_url/items/{hash_id},需在代码中计算哈希值,或每次请求传递对象以执行WHERE hash_id = md5($field_1 || $field_2 || $field_3),哪种方案更优?
专业建议
1. PUT方法用于Upsert的合理性
完全合理。PUT的语义是替换或创建资源,正好匹配你“存在则更新(追加user_id)、不存在则创建”的Upsert逻辑。需要注意两点:
- 保持幂等性:你的逻辑天然满足PUT的幂等要求,但如果需要避免重复追加同一个user_id,得在数据库层面做校验,比如用
array_position检查user_id是否已存在,再决定是否执行array_append。 - 规范响应状态码:创建新资源返回
201 Created,更新现有资源返回200 OK或204 No Content,符合REST接口规范。
2. 减少user_ids元素的HTTP方法选型
选PATCH是最优解,原因如下:
- 语义完全匹配:PATCH的核心就是部分更新资源,正好对应你只修改
user_ids数组(移除指定user_id)的需求,不需要传递整个资源对象。 - 易做权限约束:可以把请求体设计成
{"remove_user_id": "xxx"}的形式,后端只处理移除指定ID的逻辑,禁止客户端直接设置user_ids为空数组,从接口层避免手动删行的操作。 - 对比其他方法:DELETE语义是删除整个资源,和你“逐步移除元素直到自动删行”的逻辑不符;PUT需要传递完整资源对象,冗余且不符合“仅修改单个字段”的需求,所以PATCH是最适合的选择。
3. hash_id在删除场景的方案选择
优先选择在代码中计算哈希值,使用base_url/items/{hash_id}的端点方案,理由如下:
- 性能更优:代码计算哈希后直接用
hash_id作为查询条件,数据库可以利用hash_id的唯一索引快速定位行,比每次在SQL中计算md5(field_1 || field_2 || field_3)再匹配高效得多,尤其数据量较大时差异明显。 - 接口语义清晰:
base_url/items/{hash_id}明确指向唯一资源,符合REST的资源定位原则;而传递整个对象去匹配哈希,会让客户端传递冗余信息,接口设计不够简洁。 - 避免一致性问题:从你的Upsert逻辑来看,
field_1/2/3应该是资源的唯一标识,通常不会允许修改,代码计算哈希不会出现匹配失败的情况;如果后续允许修改这三个字段,那你需要重新评估hash_id的设计,但当前场景下代码计算哈希是最优解。
内容的提问来源于stack exchange,提问作者Don Draper
相关产品推荐
相关产品推荐

