尧图精选

Microsoft Graph 核心类型(Core Types)建模指南:为何 user、group、device 不应被随意扩展属性

🕒 发布时间:2026/10/1 9:17:09 📁 来源:尧图网络
API设计【免费下载链接】api-guidelinesMicrosoft REST API Guidelines项目地址https://gitcode.com/gh_mirrors/ap/api-guidelines点击查看免费下载导读本指南聚焦 Microsoft Graph REST API 设计规范中的一个关键约束user、group、device三种核心类型是整个 Graph 生态中连接度最高的中心实体因此 API 设计者不得出于便利而向它们随意追加结构属性。文章将完整讲解这一限制的动机、三种官方推荐的替代建模方案导航属性建模的三种形态并结合仓库中的 Navigation Property 模式、GuidelinesGraph.md 总纲 及配套的类型层次、Facets、Flat Bag 等模式文档给出可直接落地到 CSDL 模型的 XML 示例与取舍判断标准。读完本文你将掌握如何在 Microsoft Graph 生态内正确扩展核心实体、避免破坏 API 契约的设计陷阱。一、什么是 Microsoft Graph 的核心类型Microsoft Graph 中存在一类高度连接、处于整个生态中心位置的类型。它们在 Graph 中与大量实体存在关联因此天然处于能够容纳与其他 API 相关的结构属性的位置——其他工作负载workload在建模时很容易产生顺手把这个字段挂到 user 上的想法。在 coreTypes.md 中被正式认定为核心类型的只有三个usergroupdevice这三类实体在 Microsoft Graph 中被识别为核心类型在所有场景下向它们新增结构属性都需要提供强有力的论证will require strong justification。这是 GuidelinesGraph.md 的 Limitations on core types 章节 的细化规则总纲原文即明确user、group、device不应在没有充分理由的情况下新增任何结构属性structural properties。核心原则内在属性 vs 便利属性判断能否向核心类型追加属性的唯一标准是属性是否实体本身固有intrinsicStructural properties should be only added to these core types when they are intrinsic to the entity itself, and strictly not for the purpose of convenience due to the entitys position in Microsoft Graph.即只有当属性是实体自身天然携带、不可剥离的特征时才允许添加严格禁止因为该实体处于 Graph 的中心位置、恰好方便挂载就把它当作杂物收纳箱使用。例如user上的displayName、jobTitle、mail属于用户实体内在特征可以存在而某用户的银行账户信息某用户的会员等级这类依附于用户、却并非用户固有属性的数据就应当走替代建模方案。二、替代方案总览用新类型 导航属性建模既然不能往user、group、device上加结构属性正确的做法是创建一个新类型把拟新增结构属性所承载的信息建模进去在核心类型与新类型之间用导航属性navigation property建模关系。导航属性是 OData/CSDL 中用于描述资源与资源之间关系的机制其定义与使用细节详见 Navigation Property 模式。它的核心价值在于强类型导航属性在 CSDL 中显式声明了目标类型HTTP API 中该属性名会直接成为可追加到资源 URL 的路径段客户端无需额外信息即可访问关联资源例如/user/{userId}/manager—— 多对一关系/user/{userId}/messages—— 一对多关系对比之下传统的外键foreign key属性是弱类型机制仅存放一个 id 值客户端要遍历关系还需额外信息关联资源的发现并不容易。而导航属性 OData$expand查询参数可以让关联实体嵌套进主实体一次往返同时取回双方数据。三、实战示例为 user 建模银行账户信息原文档给出了一个完整的端到端示例假设需要为user实体建模银行账户信息其中包含accountNumber账号与routingNumber路由号两个属性。3.1 Dont直接往 user 上挂属性错误示范千万不要这样建模——这恰恰是核心类型限制要禁止的行为EntityType nameuser Property NameaccountNumber TypeEdm.string/ Property NameroutingNumber TypeEdm.string/ /EntityType这样做的后果是多方面的user是跨工作负载共享的中心类型每新增一个便利字段都会让所有消费方承受契约膨胀、语义混乱和潜在的破坏性变更风险。任何对user的改动都需要在 Microsoft Graph API review 中披露理由。3.2 Do新类型 导航属性三种官方方案正确的做法是定义新实体类型bankAccountDetail并在它与user之间建立导航关系。原文档给出三种可选形态方案一核心类型上添加包含式导航属性ContainsTargettrue先定义新实体类型EntityType namebankAccountDetail Property NameaccountNumber TypeEdm.string/ Property NameroutingNumber TypeEdm.string/ /EntityType再从user添加一个包含contained导航指向新类型EntityType nameuser NavigationProperty NamebankAccountDetail TypebankAccountDetail ContainsTargettrue/ /EntityTypeContainsTargettrue表示目标实体的生命周期由源实体控制、目标嵌入在源实体的 URL 空间内例如/users/{id}/bankAccountDetail。零或一对一、一对多子资源从属父资源的场景适合包含式关系。方案二新类型放入实体集核心类型上添加导航属性同样先定义bankAccountDetail实体类型EntityType namebankAccountDetail Property NameaccountNumber TypeEdm.string/ Property NameroutingNumber TypeEdm.string/ /EntityType将新类型放到独立的实体集或单例singleton中使其可被独立寻址EntitySet NamebankAccountDetails EntityTypebankAccountDetail再从user添加指向该类型不包含的导航属性EntityType nameuser NavigationProperty NamebankAccountDetail TypebankAccountDetail / /EntityType此方案中导航属性省略ContainsTarget其默认值即为false见 Navigation Property 模式 中关于ContainsTarget默认值的说明表示非包含关系——目标实体的生命周期独立于user例如银行账户本身可以独立存在、甚至被多个实体复用。方案三新类型放入实体集导航属性放在新类型上换一个方向在bankAccountDetail上定义指向user的导航属性EntityType namebankAccountDetail Property NameaccountNumber TypeEdm.string/ Property NameroutingNumber TypeEdm.string/ NavigationProperty Nameuser Typemicrosoft.graph.user / /EntityType并同样把新类型放入实体集EntitySet NamebankAccountInformations EntityTypebankAccountInformation注原文档此示例中实体集与实体类型的命名并不完全一致实际建模时应保证EntitySet的EntityType指向已定义的类型名。该方案把关系的宿主放在新类型上适合从银行账户反查用户的访问模式例如/bankAccountInformations/{id}/user。3.3 三种方案的取舍依据从 Navigation Property 模式 可以提炼出选择标准场景关系形态ContainsTarget推荐方案银行账户生命周期完全依附于 user无独立访问需求零或一对一 / 一对多true必须包含方案一银行账户可独立存在、独立寻址从 user 侧访问多对一 / 一对多非包含false默认方案二访问主方向是从账户反查 user多对一false方案三注意 Navigation Property 模式 中的关键约束多对一关系总是非包含的因为目标的生存期不能依赖源实体零或一对一关系必须包含可当作结构组织机制把实体的部分属性拆分出去类似复杂类型的用法但当源与目标来自不同后端 API 时优先用导航属性而非复杂类型一对多关系既可以是包含也可以是非包含子资源上的父级导航属性如parentInvoice可用于expand单次往返取回父级数据示例见GET /invoice/{invoiceId}/items/{itemId}?expandparentInvoice(selectinvoiceDate,Customer)。四、导航属性的运行时语义$ref、$expand 与绑定既然替代方案以导航属性为核心就需要理解它在 HTTP 层的完整语义详见 Navigation Property 模式 的示例部分取回关联实体导航属性默认不随实体返回除非服务显式支持或用expand请求GET /users/{id}/manager?$selectid,displayName 200 OK Content-Type: application/json { id: 6b3ee805-c449-46a8-aac8-8ff9cff5d213, displayName: Bob Boyce }仅取关联实体的引用$ref适用于只想确认关联、不需要全部属性的场景GET /users/{id}/manager/$ref 200 OK Content-Type: application/json { odata.id: https://graph.microsoft.com/v1.0/directoryObjects/6b3ee805-c449-46a8-aac8-8ff9cff5d213/Microsoft.DirectoryServices.User }用 expand 一次取回双方GET /users/{id}?selectid,displayNameexpandmanager(selectid,displayName) 200 OK Content-Type: application/json { id: 3f057904-f936-4bf0-9fcc-c1e6f84289d8, displayName: Jim James, manager: { odata.type: #microsoft.graph.user, id: 6b3ee805-c449-46a8-aac8-8ff9cff5d213, displayName: Bob Boyce } }创建/更新/清除关联POST /users Content-Type: application/json { displayName: Bob, managerodata.bind: https://graph.microsoft.com/v1.0/users/{managerId} } 201 CreatedPATCH /users/{id} Content-Type: application/json { displayName: Bob, managerodata.bind: https://graph.microsoft.com/v1.0/users/{managerId} } 204 No ContentDELETE /users/{id}/manager/$ref 204 No Content使用导航属性建模关系相比弱类型外键带来的收益原文档明确列出的强类型价值支持自动生成文档与可视化支持SDK 生成与客户端代码生成服务端无需存储重复数据跨 API 的数据一致性更好重复数据无需定期刷新。五、配套建模模式何时用新类型、用什么样的新类型创建新类型并非单一形态Microsoft Graph 提供了多种建模模式的组合选择详见 GuidelinesGraph.md 资源建模模式章节类型层次Type hierarchy一个抽象基类 每个变体一个子类型见 subtypes.md。变体互斥、各有专属属性与行为时使用。典型如directoryObject→user/group/device的派生体系——device本身就是该层次的一个子类型。Facets切面单一实体类型 每个变体一个复杂类型属性见 facets.md。变体不互斥如一个 driveItem 同时是文件又是图片时使用。Flat bag属性平铺袋单一类型容纳所有潜在属性 一个区分变体的type属性见 flat-bag.md。仅适合少量变体、弱类型可接受的情形。集合子集Collection subsets以抽象基类 派生类型表达 All / None / 包含子集 / 排除子集见 subsets.md。三种主流模式的能力对比出自 GuidelinesGraph.mdAPI 特质 \ 模式属性与行为在元数据中描述支持属性/行为组合查询构建简单类型层次是否否Facets部分是是Flat bag否否是选择要点层次模式把哪些属性对哪些变体有效的依赖完整固化在类型系统中但过滤查询需要类型转换段如$filtermicrosoft.graph.user/jobTitle eq CEOFacets 与 Flat bag 的$filter语法更简单。此外Facets/Flat bag 往往需要大量可空属性可空属性的使用边界见 nullable.md。这些模式与核心类型限制是正交的无论新类型采用层次、Facets 还是其他形态其与user/group/device的关联都必须通过导航属性完成而不能把结构属性直接塞进核心类型。六、配套命名与契约约束为核心类型建模新类型时命名同样受 naming.md 规范约束需特别注意与新类型 导航属性直接相关的条目所有标识符命名空间、entityTypes、entitySets、属性、动作、函数、枚举值必须使用 lowerCamelCase集合必须用复数名词命名如bankAccountDetails资源计数用名词 Count后缀标识符一律使用冗长命名除领域主导缩写如Url外不得使用缩写属性名应避免context、scope、resource这类在 API 领域被过度占用、失去含义的词组合词中类型后缀加在末尾如createdDateTime避免aUser、theAccount、countOfBooks这类带冠词/介词的名字对日期时间属性按DateTime/Date/Time后缀区分身份属性使用字符串类型外键用关系名 Id如subscriptionId。从契约演进角度看在 GuidelinesGraph.md 的 API contract 章节 中向既有类型添加非空属性Nullablefalse被明确列为破坏性变更而添加可空或有默认值的属性是非破坏性的。这正是核心类型限制的底层逻辑之一对user这类被全生态共享的类型任何属性追加都会扩散到所有消费方因此更要走新类型 导航的隔离式演进路线。七、总结围绕 Microsoft Graph 的核心类型建模可以收敛为三条可执行规则默认不扩展user、group、device是核心类型向它们添加结构属性需要强有力的论证且仅当属性对实体本身固有时才被允许。优先新类型 导航需要表达依附于核心实体却非其固有属性的信息时先创建新类型再按访问模式选择——核心类型上包含式导航方案一、核心类型上非包含导航方案二、新类型上反向导航方案三。守契约、用强类型借助导航属性的强类型机制获得 SDK 生成、文档自动化和数据一致性收益同时遵循 命名规范 与 破坏性变更约束让 API 可持续演化。相关规范文本可在仓库中继续深读核心类型专文、总纲中的限制章节、导航属性模式、以及与之配合的 类型层次、Facets、Flat Bag 和 集合子集 模式文档。赞分享API设计【免费下载链接】api-guidelinesMicrosoft REST API Guidelines项目地址https://gitcode.com/gh_mirrors/ap/api-guidelines点击查看免费下载相关推荐Livewire Synthesizer 深入指南为 Laravel 组件属性扩展任意数据类型支持Livewire Synthesizer 深入指南为 Laravel 组件属性扩展任意数据类型支持 导读 Synthesizer合成器是 Livewire后端前端在 Meteor 中启用核心包 TypeScript 类型zodern:types 与 using-core-types 完整指南在 Meteor 中启用核心包 TypeScript 类型zodern:types 与 using core types 完整指南 导读 本指南围绕 Mete后端前端开发工具移动开发React属性类型检查终极指南深入理解prop-types库的核心机制React属性类型检查终极指南深入理解prop types库的核心机制 prop types是React生态中用于运行时类型检查的核心库它能够帮助开发者在开上一篇Windows HEIC缩略图预览完整指南让iPhone照片在Windows完美显示下一篇Windows HEIC缩略图终极解决方案如何让Windows资源管理器完美显示iPhone照片预览创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →