如何在RESTful API中指定某些资源类型的子类型



(在REST API教程和StackOverflow关于REST API最佳实践的博客等指南中找不到教程或对此的明确描述,关于子类型的SO问题讨论了另一个角度。)

我们应该如何在REST API中指定相同资源类型的不同子类型?

例如,在CRM应用程序中考虑不同类型的联系人:B2C客户是个人,B2B客户是公司,B2E人员是我们自己公司的员工。

我可以想出两种方法在URL路径中指定它。主要区别在于它如何影响文档中的必需/可选资源属性列表。类似地,这也渗透到处理API请求的代码中。验证某些属性是允许的、必需的还是可选的,在不同资源类型的实体之间可能会有所不同。

在资源URL路径中显式指定子类型

https://api.example.com/contacts/b2b/:id
https://api.example.com/contacts/b2c/:id
https://api.example.com/contacts/b2e/:id

或者类似的东西

https://api.example.com/b2b-contacts/:id
https://api.example.com/b2c-contacts/:id
https://api.example.com/b2e-contacts/:id\

其优点是API契约可以非常具体地定义,并针对每种类型的资源进行定制。一些属性是常见的(idnamestreetemail…),而其他属性仅特定于一种类型(cocdunsvat-id…用于B2B联系人,employee-iddepartmentdob…用于员工)。API文档可以清楚地指定每个子类型/资源的允许/必需/可选属性列表。

(不要以正确的方式登记公司联系人或员工……这只是一个例子。)

将不同的子类型分组在URL路径中的公共资源下

https://api.example.com/contacts/:id
,对象(符合REST术语的资源状态表示)指定要交换的属性之一中的子类型。

这更通用、更灵活,但API文档需要明确指定每个联系人类型需要/可选的属性。type也将是一个属性。

因此,在这种情况下,文档可能会列出等属性

必需是[/tr>是没有是是//tr>
属性 适用于类型
id 所有(或:B2B、B2C、B2E)
名称 所有
电子邮件 所有
联系人类型 全部
员工id B2E
dob B2E,B2C
部门 B2E
coc B2B
传真 B2B、B2C
ssn B2C

好啊老API资源多态性。。。这是一个棘手的问题:-)

一般来说,我想说这个问题的答案在一定程度上取决于如何使用API。对此的一个很好的测试是API的用户希望如何列出;"联系人";。用户会想";列出所有联系人";?或者他们会想";列出所有B2B联系人";并且从不要求能够将B2B联系人与B2E联系人一起列出?

如果将它们隔离到单独的资源中(例如,B2BContactB2EContact等),则用户无法浏览所有联系人(任何类型)。然而,如果组合为单一类型(例如GET /contacts?filter=type:B2B或类似类型),则它们仍然可以浏览联系人。

如果不同类型的联系人(B2B/B2E/B2C)都有根本的不同,那么将它们组合成一种资源类型是错误的。现在,您有一些属性只适用于特定类型,但不适用于所有类型——然而,这就是今天存在的。更有可能的是,随着时间的推移,属性的数量会增长,并且你会对API开始时有意义的事情感到困惑,但现在不再那么多了,这可能会让用户非常沮丧。

关于细节。。。

如果要将它们划分为多个资源类型,我建议将它们简单地视为单独的资源(GET /b2b-contacts/GET /b2c-contacts/等),而不是同一资源的单独类型(例如GET /contacts/b2b)。这样,如果他们将来需要偏离另一个,就没有什么能把资源联系在一起,因为"当API启动时,他们都碰巧是联系人;。

如果您要将这些资源类型与type字段组合为一个单一的资源类型,以指示B2B/B2C/B2E方面:

  1. 对于只适用于特定类型的字段,请尝试将它们放入";容器";对于该类型的数据。例如,fax仅适用于B2B类型的联系人,因此(此处使用Typescript,如果不是您的问题,请抱歉):
class Contact {
id: string;
type: 'b2b' | 'b2c' | 'b2e';
// ...
metadata: B2BContactMetadata | B2CContactMetadata | ...;
}
class B2BContactMetadata {
fax: string;
}
// ...
  1. 同样的事情也适用于应用于3种类型中的2种类型的字段。如果fax突然对B2B和B2E有意义,但对B2C没有意义,您只需将其添加到B2EContactMetadata:
class B2BContactMetadata {
fax: string;
}
class B2EContactMetadata {
fax: string;
}
  1. 在将字段提升到Contact级别时要非常小心。如果某件事真的是所有当前和未来联系人的基础,那么它就属于最高级别。如果它恰好适用于您现在拥有的所有联系人类型,但有可能对新类型没有意义,那么将其放入特定类型的元数据区域(或其中一个)

关于此主题的更多探索,API设计模式的第16章介绍了API中的多态性(https://livebook.manning.com/book/api-design-patterns/chapter-16)

相关内容

  • 没有找到相关文章

最新更新