ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Grafast 标准步骤 `get()`:安全提取步骤属性的首选方案与源码级原理剖析

Grafast 标准步骤 `get()`:安全提取步骤属性的首选方案与源码级原理剖析 后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载get($step, attrName)是 Grafast 计划Plan体系中最常用的基础步骤之一它返回一个代表$step的attrName属性的新步骤。本文以 get.md 为核心结合 get.ts、access.ts 的源码实现与 dcc-schema.ts 等测试用例讲解其用法、与access()的取舍、类型安全机制以及必须遵守的可信路径安全边界。读完本文你将能够在自己的计划解析器plan resolver中正确、安全地使用get()组合出属性读取步骤。get()是什么get($step, attrName)会返回一个表示$step的attrName属性的步骤。它的分发逻辑非常简单如果$step自身具备get方法就调用$step.get(attrName)否则回退到access($step, attrName)。const $userId get($user, id);这行代码表示从$user步骤对应的对象中取出id属性产出一个新的步骤$userId后续计划可以继续把它作为依赖使用。基本用法import { get } from grafast; // $user 是一个表示用户对象的步骤例如 loadOne 的返回 const $userId get($user, id); const $userName get($user, name);get()的签名简化定义为// Simplified definition function get($step: Step, attr: string): Step { return get in $step typeof $step.get function ? $step.get(attr) : access($step, attr); }从这一简化定义可以看出get()的本质是一个智能分发器优先走步骤自身实现的get方法只有步骤没有get方法时才退化为通用的属性访问。为什么优先用get()而不是access()配套文档 access.md 明确给出了警告在一般情况下应该优先使用get()而不是access()。原因在于很多步骤依赖.get()/.at()方法才能正常工作。例如一个loadOne()步骤如果你不调用.get(attribute_name)它就不会知道需要从后端请求attribute_name这个属性最终可能得到意外的null/undefined。access()会绕过步骤自身的.get()/.at()方法意味着关键数据可能不会从数据源被拉取。从源码看loadOne.ts 的get方法通过cacheStep(get, attr, ...)实现并在计划阶段通过getAccessMap()记录被访问的属性名——这些属性名随后会透传给你的业务层回调让你可以只向后端请求真正需要的字段。如果绕过get()直接使用access()属性追踪机制就会失效。所以步骤有.get()/.at()方法时永远优先使用它们只有当你明确知道自己在做什么时才使用access()而get()会自动帮你完成这个能调用$step.get(attr)就调用、不能则回退access的决策因此它比裸用access()更安全、更省心。源码实现剖析类型安全与运行时分发仓库中的真实实现位于 get.ts它在简化定义之上还提供了精密的 TypeScript 类型推导export function get TStep extends Step, TAttr extends StepGetKeysTStep string, ($step: TStep, attr: TAttr): GetResultTStep, TAttr { return get in $step typeof $step.get function ? $step.get(attr) : (access($step, attr) as any); }运行时逻辑与文档中的简化定义完全一致但类型层面做了大量工作1.HasInferredGet步骤主动声明可推导的属性类型interface HasInferredGetTStepByKey extends Recordstring, Step { __inferGet?: TStepByKey; }这是类型层面的技巧__inferGet只是一个声明用的键运行时并不存在实际值。实现它的步骤类可以显式声明通过get(key)能拿到什么类型的步骤。源码注释给出了推荐范式class MyStep extends Step { __inferGet?: { [TKey in keyof MyData]: MySpecialStepMyData[TKey]; }; getTKey extends keyof MyData(key: TKey): MySpecialStepMyData[TKey] { // ... } }AccessStep就是这样一个例子在 access.ts 中声明__inferGet?: { [TAttr in keyof TData]: AccessStepTData[TAttr] };2.StepGetKeys推导步骤支持哪些属性键type StepGetKeysTStep extends Step TStep extends HasInferredGetinfer UStepByKey ? keyof UStepByKey : TStep extends { get(attr: infer U): any } ? U : TStep extends Stepinfer UData | null | undefined ? keyof UData : never;优先级依次为若实现了HasInferredGet取显式声明的键集合否则若存在get方法取该方法接受的参数类型否则根据步骤编码的数据类型UData提取其键都不满足则为never。3.GetResult推导返回步骤的类型type GetResultTStep extends Step, TAttr extends StepGetKeysTStep TStep extends HasInferredGetinfer UStepByKey ? TAttr extends keyof UStepByKey ? UStepByKey[TAttr] : never : TStep extends { get(attr: any): infer UGetStep } ? UGetStep : TStep extends Stepinfer UData | null | undefined ? TAttr extends keyof UData ? StepUData[TAttr] : never : never;GetResult与StepGetKeys一一对应显式声明优先其次取get方法的返回类型最后回退为StepUData[TAttr]。这使得get($user, id)返回的步骤在类型上精确反映用户数据中id属性的类型为后续计划编写提供完整的类型安全保障。回退目标AccessStep的底层机制当步骤没有get方法时get()回退调用access()产出AccessStep定义于 access.ts。理解它的实现有助于你判断什么时候可以放心依赖这一回退路径。属性路径与运行时解构AccessStep记录一个path: (string | number | symbol)[]并在finalize()阶段通过constructDestructureFunction生成高效的取值函数access.ts路径长度为 0直接返回整个值路径长度为 1 / 2 / 3分别生成value?.[p0]、value?.[p0]?.[p1]、value?.[p0]?.[p1]?.[p2]的优化代码更长的路径退化为逐层遍历的slowlyExtractValueAtPath。也就是说常见的浅层属性访问在运行时会被编译成最精简的取值表达式性能上有专门优化。步骤去重与优化AccessStep通过peerKey由fallback有无、路径中是否含 symbol、digestKeys(path)摘要共同决定参与计划级的步骤去重access()函数内部对无fallback且路径无 symbol 的情况通过operationPlan.cacheStep(parentPlan, GrafastInternal:access(), pathKey, ...)进行缓存access.ts相同路径的属性访问可以复用同一个步骤optimize()实现了访问的访问合并为一次访问若AccessStep的依赖也是AccessStep则把路径拼接后直接访问最终来源access.ts。这些机制保证了即使大量使用get()/access()计划图中也不会出现冗余的属性读取节点。AccessStep自带的.get()与.at()AccessStep自身也提供两个方法.get(key)假定解析后的 JSON 值是一个对象取出key对应的值返回新的AccessStep仅接受字符串见 access.ts.at(index)假定值是一个数组取出index处的元素access.ts。因此对get()返回的步骤还可以继续链式调用get()——此时由于AccessStep实现了get方法外层get()会走调用$step.get(attr)的分支而非再次回退。安全边界只能访问可信路径文档用醒目 danger 块强调只能访问可信的路径如果使用不当get()可能带来意想不到的结果甚至引入安全问题。这是因为 JavaScript 对象上存在大量继承自原型链的危险键例如非穷尽列表constructor/prototype/__proto__toString/valueOfhasOwnProperty/isPrototypeOf__defineGetter__/ 等等这些键在普通 JS 对象上几乎总是存在且行为特殊。如果允许用户输入来构造访问路径攻击者就可能借由__proto__、constructor.prototype之类的路径触发原型污染或访问到本不该暴露的内部属性。因此必须遵守以下纪律只在 JSON-like 数据上使用get()最好数据对象具有null 原型Object.create(null)路径键必须是你信任的常量绝不要使用用户提供的数据作为路径。源码 access.ts 的注释也给出了完全一致的提醒它可能引发未预期的结果甚至安全漏洞只能用于 JSON-like 数据并且要遵守assertSafeToAccessViaBraces所述的命名约定。实战中的典型用法在仓库测试代码中可以大量看到get()的实战模式例如 dcc-schema.tsconst $id get($activeCrawler, id); const $items get($crawler, items); const $number get($floor, number); const $__typename get($location, type); const $id get($item, id);在 conformance/utils.ts 中还有结合__typename的用法$__typename: get($specifier, __typename),这些例子展示了get()最典型的三个场景取对象的业务字段id、name、items等配合内省字段__typename做类型判断作为其他步骤的输入例如loadOne(get($lootDatum, lootBoxId), { load: ... })——把一个属性步骤直接作为数据加载的键。由于get()返回的仍然是步骤Step它可以无缝参与计划图组合作为依赖传入loadOne、lambda、condition、inhibitOnNull等其他标准步骤这正是 Grafast计划即数据流的核心范式。小结get($step, attrName)是 Grafast 中提取步骤属性的首选标准步骤优先调用$step.get(attrName)回退到access($step, attrName)相比裸用access()get()能确保loadOne等步骤正常进行属性追踪与按需取数避免意外的null/undefined类型层面通过HasInferredGet/StepGetKeys/GetResult三重机制提供精确推导运行时回退的AccessStep内置路径解构优化、步骤缓存与去重性能有保障安全第一只在可信、JSON-like、最好为 null 原型的数据上使用路径必须是你信任的常量绝不能使用用户输入。相关资源索引标准步骤文档get.md、access.md、loadOne.md核心源码get.ts、access.ts、loadOne.ts测试示例dcc-schema.ts、conformance/utils.ts赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐Grafast 标准步骤详解each() 列表转换与 applyTransforms() 的协作机制Grafast 标准步骤详解 each 列表转换与 applyTransforms 的协作机制 Grafast 是 Graphile Crystal 仓库中负后端API网关Element Steps 步骤条组件完全指南从基础用法到源码级原理剖析Element Steps 步骤条组件完全指南从基础用法到源码级原理剖析 Steps 步骤条 是 ElementA Vue.js 2.0 UI Toolki前端UI组件设计系统Grafast 标准步骤 list将多个 Plan 组合为列表 Plan 的完整指南Grafast 标准步骤 list 将多个 Plan 组合为列表 Plan 的完整指南 list 是 Grafast 内置的标准步骤standard ste后端API网关创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表