如何在Restful API中指定某些资源类型的亚型?

发布于 2025-01-26 20:46:55 字数 2515 浏览 1 评论 0 原文

(在 stackoverflow在REST API上的最佳实践上的博客和A subtypes上的问题讨论另一个角度。)

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

例如,在CRM应用程序中考虑不同类型的联系人:B2C客户是个人,B2B客户的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 < br> https://api.example.com/b2c-contacts/:id
https://api.example.com/b2e-contacts/:id \ \ \ \ \ \ \

优势是可以非常具体地定义API合同,并根据每种类型的资源量身定制。某些属性是常见的( ID name street 电子邮件 ...),而其他属性是特定的仅适用于一种类型( coc duns vat-id ,...用于B2B联系人,雇员>雇员-ID ,<代码>部门 dob ,...适用于员工)。 API文档可以清楚地指定每个子类型/资源的允许/必需/可选属性列表。

(不要深入了解注册公司联系人或员工的正确方法...这只是一个示例。)

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

https://api.example.com/contacts/ :id
使用对象(资源状态表示符合其余条件)在要交换的属性之一中指定子类型。

这是更通用和灵活的,但是API文档需要清楚地指定每种联系类型所需的属性/可选的属性。 类型也将是一个属性。

,在这种情况下,该文档可能会列出

属性 适用于 所需
类型ID 的属性(或:B2B,B2C,B2E) 是否
name ash yesememair
yes email 全部 no no
Contact-type 因此
... 名称name 。
B2C 是的

(同样,不要批评这个CRM示例的细节和适当性。这只是一个例子。我知道我们不应该存储社会保险号,而B2B客户通常也包含/参考某人。)

问题是

什么处理此操作的常见方法是什么?如何记录它,如何处理属性验证(允许,必需,可选)?

(Couldn't find a tutorial or clear description of this in guides like REST API Tutorial and StackOverflow's blog on REST API best practices, and a SO question on subtypes discusses another angle.)

How should we specify different subtypes of the same resource type in a REST API?

For example consider different kinds of contacts in a CRM application: B2C customers which are persons, B2B customers who are companies, and B2E persons which are employees of our own company.

I can think of two ways of specifying that in the URL path. The main distinction is how it affects the list of required/optional resource attributes in the documentation. Similarly, this oozes through in the code handling the API requests. The validation whether some attributes are allowed, required or optional likely differs between entities of different resource types.

Specify subtypes explicitly in the resource URL path

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

or something like

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

The advantage is that the API contract can be defined very specifically, and tailored to each type of the resource. Some attributes are common (id,name,street,email,...) whereas other attributes are specific only to one type (coc, duns, vat-id, ... for B2B contacts, and employee-id, department, dob, ... for employees). The API documentation could clearly specify the list of allowed/required/optional attributes for each subtype/resource.

(Don't dive into the proper way to register company contacts or employees... it's just an example.)

Group different subtypes under the common resource in the URL path

https://api.example.com/contacts/:id
with the objects (resource state representations conforming to the REST parlance) specifying the subtype in one of the attributes being exchanged.

This is more generic and flexible, but the API documentation needs to clearly specify which attributes are required/optional for each contact type. type will be an attribute too.

So, in this case, the documentation is likely to list attributes like

Attribute Applicable for types Required
id All (or: B2B, B2C, B2E) Yes
name All Yes
email All No
contact-type All Yes
... ... ...
... ... ...
employee-id B2E Yes
dob B2E, B2C No
dept B2E No
... ... ...
coc B2B Yes
fax B2B, B2C No
... ... ...
ssn B2C Yes

(Again, don't critique the specifics and appropriateness of this CRM example. It's just an example. I know we shouldn't store a social security number commonly, and B2B customers typically contain/refer to a person as well.)

Question

What's the common way to handle this? How to document it, and how should attribute validations (allowed, required, optional) be handled?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

感情洁癖 2025-02-02 20:46:55

啊,好的旧的API资源多态性...这是一个棘手的:-)

总的来说,我要说的是这个问题的答案取决于如何使用API​​。一个很好的测试是API的用户如何列出“联系人”。用户是否需要“列出所有联系人”?还是他们想“列出所有B2B联系人”,而不需要与B2E联系人列出B2B联系人的能力?

如果将它们隔离为单独的资源(例如 B2BContact b2econtact 等),则用户无法浏览所有(任何类型)的联系人。但是,如果将它们合并为单一类型(例如, get/contacts?filter = type:b2b 或类似的内容),它们仍然可以浏览联系人。

如果不同类型的联系人(B2B / B2E / B2C)在根本上都是真正不同的,则将它们组合成单个资源类型是错误的。目前,您有一些仅适用于特定类型的属性,但不是全部 - 但这就是今天 。随着时间的流逝,属性的数量将增加,您会对API启动但不再那么多的事情感到有意义的事情感到非常困惑,这对用户来说可能会令人沮丧。

在细节上...

如果您要将它们分为多种资源类型,我建议将它们视为简单的分开资源( get/b2b-contacts/ get/b2c-contacts/等),而不是同一资源的单独类型(例如, get/contacts/b2b )。这样,如果他们将来需要偏离另一个人,那么没有什么可以将资源绑在一起,因为“当API启动时,它们恰好是联系人”。

如果您将它们与类型字段相结合,以指示

  1. 仅适用于特定类型的字段, 尝试将它们放入该类型数据的“容器”中。例如,传真仅适用于B2B类型联系人,因此(在此处使用TypeScript,如果不是您的果酱,则对不起):
class Contact {
  id: string;
  type: 'b2b' | 'b2c' | 'b2e';
  // ...

  metadata: B2BContactMetadata | B2CContactMetadata | ...;
}

class B2BContactMetadata {
  fax: string;
}

// ...
  1. 适用于适用于3种类型的2个字段。如果传真突然对于B2B和B2E而不是B2C是有意义的,则您只需将其添加到B2ECONTACTMETADATA:
class B2BContactMetadata {
  fax: string;
}

class B2EContactMetadata {
  fax: string;
}
  1. 非常小心地将字段引入 Contact> Contact> Contact> Contact> Contact -Level。如果某事对当前和将来的所有联系真正至关重要,那么它属于该最高级别。如果恰好适用于您现在拥有的所有联系类型,但是对于新型类型来说,它没有意义,然后将其放在特定于类型的元数据区域(或其中之一)。

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

Ah good old API resource polymorphism... This is a tricky one :-)

In general, I'd say the answer to this question depends a bit on how the API is going to be used. A good test for this is how the users of the API want to list the "contacts". Will users want to "list all contacts"? Or will they want to "list all B2B contacts" and never require the ability to list B2B contacts alongside B2E contacts?

If you isolate these into separate resources (e.g., B2BContact, B2EContact, etc), then there's no way for users to browse through all the contacts (of any type). However they can still browse contacts if combined into a single type (e.g., GET /contacts?filter=type:B2B or something similar).

If the different types of contacts (B2B / B2E / B2C) are all truly fundamentally different, it'd be a mistake to combine them into a single resource type. Right now you have some properties that apply only to specific types but not all -- however that's what exists today. It's far more likely that over time the number of properties will grow, and you'll have quite a bit of confusion about things that made sense together when the API started but not so much anymore, which can be very frustrating for users.

Onto the specifics...

If you're going to separate these into multiple resource types, I'd suggest treating them as simply separate resources (GET /b2b-contacts/, GET /b2c-contacts/, etc) and not separate types of the same resource (e.g., GET /contacts/b2b). This way, if they need to deviate from another in the future, there's nothing tying the resources together because "they all happened to be contacts when the API started".

If you're going to combine these as a single resource type with a type field to indicate the B2B/B2C/B2E aspect:

  1. For fields that only apply for specific types, try putting them into a "container" for data of that type. For example, fax only applies to B2B type contacts, so (using Typescript here, sorry if that's not your jam):
class Contact {
  id: string;
  type: 'b2b' | 'b2c' | 'b2e';
  // ...

  metadata: B2BContactMetadata | B2CContactMetadata | ...;
}

class B2BContactMetadata {
  fax: string;
}

// ...
  1. The same thing applies for fields that apply to 2 of the 3 types. If fax suddenly makes sense for B2B and B2E but not B2C, you'd simply add it to the B2EContactMetadata:
class B2BContactMetadata {
  fax: string;
}

class B2EContactMetadata {
  fax: string;
}
  1. Be very careful about hoisting fields up into the Contact-level. If something is truly fundamental to all current and future contacts, then it belongs at that top level. If it just happens to apply to all contact types you have now, but there's a chance it wouldn't make sense for a new type, then put it in the type-specific metadata area (or one of them).

For more exploration on this topic, Chapter 16 of API Design Patterns covers Polymorphism in APIs (https://livebook.manning.com/book/api-design-patterns/chapter-16)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文