news 2026/9/10 1:23:56

Backstage 软件目录(Software Catalog)REST API 完全指南:实体与 Location 接口详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Backstage 软件目录(Software Catalog)REST API 完全指南:实体与 Location 接口详解

Backstage 软件目录(Software Catalog)REST API 完全指南:实体与 Location 接口详解

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

本文面向需要将外部系统接入 Backstage 软件目录、或希望深入理解目录数据如何被查询与维护的开发者。Backstage 软件目录后端提供了一套基于 JSON 的 REST API,外部系统可以通过这套接口查询实体(Entities)、管理 Location、触发刷新与校验等操作。读完本文,你将掌握by-query谓词过滤、字段裁剪、游标分页、全文检索等核心能力的完整用法,并能结合仓库源码理解其底层实现。

总览:API 的形态与访问前提

软件目录(Software Catalog)是 Backstage 中用于建模软件及其所有者(组件、API、系统、资源、用户、组等)的核心服务。其后端暴露的是一套基于 JSON 的 REST API,供外部系统直接调用。完整的 OpenAPI 规范定义位于仓库的 openapi.yaml(约 1500 行的 OpenAPI 3.1 规范,同时包含请求校验所需的全部参数与响应 schema)。

从功能上划分,这套 API 主要分为两个大的功能组:

  • Entity(实体)类接口:直接读取、查询、删除、刷新实体,以及批量获取、校验实体;
  • Locations(位置)类接口:管理 Location(即目录数据来源的注册信息),例如把某个 YAML 文件或 Git 仓库注册为目录的数据源。

注意:官方文档明确说明,这份页面目前只覆盖 API 中最常用的一部分,仍在持续完善中。如需最权威、最完整的定义,请以 openapi.yaml 为准。

基准 URL(Base URL)

文档中出现的所有 URL 路径都假定位于某个指向你目录实例的基准 URL 之上。例如:

  • 文档给出路径为/entities
  • 本地开发时目录通常位于http://localhost:7007/api/catalog
  • 那么完整 URL 就是http://localhost:7007/api/catalog/entities

生产环境中的实际 URL 因组织而异,但常见的形态是:应用配置中的backend.baseUrl加上/api/catalog后缀。这也是大多数 Backstage 部署中目录后端的标准挂载路径。

认证方式

部分或全部端点可能接受或要求携带Authorization头,值为Bearer <token>,其中的 token 应为由 Backstage 的 identity API 返回的 Backstage token。从 openapi.yaml 的securitySchemes可以看到,该 API 定义了一个名为JWT的 HTTP Bearer 安全方案(bearerFormat: JWT),且每个路径都同时声明了匿名({})与带 JWT 两种安全选项,说明部分接口在未认证时也可能可用,具体取决于部署的权限策略。

另外,从 createRouter.ts 可以看到,目录支持catalog.readonly配置项,开启后目录会进入只读模式,禁止写入类操作。

实体(Entities)接口

实体类接口读取的是最终实体(final entities)——即所有处理(processing)与拼接(stitching)流程完成之后的输出结果,而非最初被摄取(ingested)的原始数据。关于这一过程与两者区别的详细说明,可参阅 The Life of an Entity。这意味着:一个实体只有在走完整个处理管线并落入"最终实体"集合后,才会通过目录 API 对外可见。

关系(Relation)响应格式

目录 API 的响应中使用targetRef字段表示关系目标。文档特别强调:

已弃用的target对象以及catalog.enableRelationsCompatibility设置已被移除。如果外部目录 API 消费者仍在读取relation.target,请改为使用relation.targetRef

targetRef的值是一个完整的实体引用(entity reference),其格式为[<kind>:][<namespace>/]<name>的完整形态,例如group:default/ops。关于引用格式的完整说明,参见 Entity References。在 openapi.yaml 的EntityRelationschema 中,targetReftype均为必填字段,印证了这一响应约定。

GET /entities/by-query—— 分页查询实体

这是查询实体的主力端点,支持以下查询参数:

参数作用默认值
filter选择实体的子集(详见"过滤"小节)
fields只选择每个实体数据结构的一部分(字段裁剪)返回完整实体
limit限制返回的实体数量20
orderField决定实体的排序方式(详见"排序"小节)按内部uid
fullTextFilterTerm/fullTextFilterFields按文本过滤实体(全文检索)见"全文过滤"小节
cursor用于获取下一批/上一批实体的游标(分页)
totalItems是否计算响应中的totalItems字段,取值为include(默认)或excludeinclude

其中totalItems参数在 openapi.yaml 中有详细说明:对于大型目录,计算总数可能比较昂贵;如果调用方不需要(例如只做装饰性展示的游标分页 UI),可以传入exclude跳过统计。未来还可能新增近似模式等取值。

响应为如下形式的 JSON:

{ "items": [{ "kind": "Component", "metadata": { "name": "foo" } }], "totalItems": 4, "pageInfo": { "nextCursor": "a-cursor", "prevCursor": "another-cursor" } }

其中items是分页过滤后的实体列表,totalItems是符合条件的实体总数,pageInfo中携带用于继续翻页的游标。

过滤(Filtering)

你可以传入一个或多个过滤集合(filter sets),每个集合由若干条件组成。同一集合内的条件全部满足才为真(条件之间是 AND 关系);只要至少一个过滤集合为真,实体就会进入结果集(集合之间是 OR 关系)。

示例:

/entities/by-query?filter=kind=user,metadata.namespace=default&filter=kind=group,spec.type 返回匹配以下条件的实体: 过滤集合 1: 条件 1: kind = user AND 条件 2: metadata.namespace = default OR 过滤集合 2: 条件 1: kind = group AND 条件 2: spec.type 存在

每个条件要么是<key>形式,要么是<key>=<value>形式。前者断言某个键存在(值不限),后者断言该键存在且具有特定值。所有检查都是大小写不敏感的

在所有情况下,key 都是针对实体数据某一片段的简化 JSON 路径:路径的每一段是对象的一个键,遍历时也会深入数组内部。有两个特殊形式:

  • 数组中的简单值项(如字符串):匹配的等价形式是"键为该项字符串、值为字符串true"的键值对;
  • 关系:可以使用relations.<type>=<targetRef>形式对关系进行匹配。

看一个简化的例子来说明这个概念。对于下面的实体数据:

{ "a": { "b": ["c", { "d": 1 }], "e": 7 } }

以下任一条件都能匹配它:

  • a
  • a.b
  • a.b.c
  • a.b.c=true
  • a.b.d
  • a.b.d=1
  • a.e
  • a.e=7

更多贴近真实场景的例子:

  • 返回所有孤儿实体(orphaned):

    /entities/by-query?filter=metadata.annotations.backstage.io/orphan=true

  • 返回所有用户和组:

    /entities/by-query?filter=kind=user&filter=kind=group

  • 返回所有 service 类型的组件:

    /entities/by-query?filter=kind=component,spec.type=service

  • 返回所有带java标签的实体:

    /entities/by-query?filter=metadata.tags.java

  • 返回ops组的全部成员用户(注意这里使用的是该组的完整引用):

    /entities/by-query?filter=kind=user,relations.memberof=group:default/ops

全文过滤(Full Text Filtering)

通过fullTextFilterTerm查询参数可以对实体字段进行文本搜索。它会对实体 YAML 字段中的值执行大小写不敏感的 substring 匹配

需要特别注意默认行为:当未指定fullTextFilterFields参数时,搜索会作用于当前的排序字段(来自orderField),如果连排序字段也未设置,则作用于metadata.uid。这意味着不显式指定字段时,搜索可能不会命中你预期的字段

要控制搜索范围,请通过fullTextFilterFields查询参数传入逗号分隔的实体字段路径列表:

  • fullTextFilterTerm—— 要搜索的文本(大小写不敏感、子串匹配)
  • fullTextFilterFields—— 要搜索的实体字段路径列表(逗号分隔,例如metadata.name,metadata.title

示例:

/entities/by-query?fullTextFilterTerm=my-service&fullTextFilterFields=metadata.name,metadata.title 返回 metadata.name 或 metadata.title 包含 "my-service" 的实体

真实场景示例:

  • 按名称搜索组件:

    /entities/by-query?filter=kind=component&fullTextFilterTerm=payment&fullTextFilterFields=metadata.name

  • 同时跨 name 与 title 搜索:

    /entities/by-query?filter=kind=system&fullTextFilterTerm=platform&fullTextFilterFields=metadata.name,metadata.title

  • 与其他过滤器组合(例如限定某个组拥有的实体):

    /entities/by-query?filter=kind=component,relations.ownedBy=group:default/my-team&fullTextFilterTerm=api&fullTextFilterFields=metadata.name

注意:全文过滤与基于游标的分页是互斥的。当提供了cursor时,fullTextFilterTermfullTextFilterFields会被忽略——游标本身已经编码了初始请求中的原始过滤参数。

字段选择(Field Selection)

默认情况下接口返回完整的实体。通过fields查询参数可以指定保留实体的哪些部分,这能让响应更小、传输更快,并可能让目录执行更高效的查询。

参数值为逗号分隔的简化 JSON 路径列表(与过滤中的路径规则相同)。每个路径对应一个值或子树根的键,输出时其余部分会被裁剪掉。例如,指定?fields=metadata.name,metadata.annotations,spec会:

  • 保留每个实体metadata中的nameannotations字段(结果是一个至多含两个键的对象);
  • 完整保留spec
  • 裁剪掉所有其他根级内容,如relations

真实场景示例:

  • 只返回足以构成每个实体完整 ref 的数据:

    /entities/by-query?fields=kind,metadata.namespace,metadata.name

在 openapi.yaml 中,fields参数被声明为数组类型并带有两个官方示例:"Get name and the entire relations collection"(metadata.name+relations)与 "Get kind, name and namespace",可供参考。

排序(Ordering)

默认情况下实体按其内部uid排序。可以通过orderField查询参数自定义排序。

例如,按实体名称返回:

/entities/by-query?orderField=metadata.name,asc

每个参数后可以跟asc(升序字典序)或desc(降序、反转字典序)。在 openapi.yaml 中,orderField被描述为[field, order]的二元组数组,官方示例包括metadata.name,asc(按名称升序)和spec.owner,desc(按 owner 降序)。

游标分页(Pagination)

可以通过cursor查询参数对实体集合执行游标式分页。cursor的值会出现在响应的pageInfo属性中:

"pageInfo": { "nextCursor": "a-cursor", "prevCursor": "another-cursor" }
  • 如果nextCursor存在,可以用它获取下一批实体;
  • 同理,如果prevCursor存在,可以用它获取上一批实体。

需要强调的是:[filterorderFieldfullTextFilter] 与cursor是互斥的。这意味着在传入了cursor时,不能更改filterorderFieldfullTextFilter中的任何一个——更改这些属性会影响分页。如果它们与cursor同时指定,只有后者会被考虑。

POST /entities/by-query—— 谓词式查询

该端点支持与 GET 变体相同的功能,但参数放在 POST body 中,从而不必受 URL 长度限制的约束。此外,它还支持一种更高级、更具表达力的查询格式——谓词过滤(见下文)。响应格式与 GET 变体完全相同。

从 createRouter.ts 可以看到,该路由在router.post('/entities/by-query', ...)中实现,内部通过parseEntityQuery等请求解析工具处理传入的谓词表达式。

通过过滤器谓词(Filter Predicate)查询

可以在请求中传入过滤器谓词来选择目录中实体的子集。谓词由一棵可选的逻辑表达式树(使用$all$any$not)构成,树的最末端是过滤集合(filter sets),集合内可以使用自定义匹配器(如$exists$in$hasPrefix$contains)。

下面是一个过滤器谓词表达式的例子:

{ "query": { "$all": [ { "kind": "Component", "spec.type": { "$in": ["service", "website"] } }, { "$not": { "metadata.annotations.backstage.io/orphan": "true" } } ] } }

一个过滤集合是键为点分隔路径、值为原始值(字符串、数字或布尔)或自定义匹配器的对象。例如一个简单的过滤集合:

// 对给定实体而言,以下条件必须全部为真(它们之间是隐式 AND) { // kind 字段与字面量做大小写不敏感的匹配 "kind": "Component", // spec 内的 type 字段使用自定义匹配器,见下文 "spec.type": { "$in": ["service", "website"] } }

查询的根节点始终是一个对象,无论其中是否有逻辑表达式树。单个键以$符号开头的节点具有特殊含义。以下是全部逻辑运算符与匹配器:

  • $not:逻辑取反。其值必须是一个单独的表达式。

    // 匹配 kind 不是 Component 的实体 { "$not": { "kind": "Component", } }

    注意:$not不能用在右侧的值匹配器中:

    // ❌ 错误 { "kind": { "$not": "Component" } } // ✅ 正确 { "$not": { "kind": "Component" } }
  • $all:要求所有给定表达式都匹配实体。其值必须是表达式数组。

    // 匹配同时具有 kind Component 与 type website 的实体 { "$all": [ { "kind": "Component" }, { "spec.type": "website" } ] }

    空数组总是匹配所有实体。

  • $any:要求一组表达式中至少有一个匹配给定实体。其值必须是表达式数组。

    // 匹配 kind 为 Component 或 type 为 website 的实体 { "$any": [ { "kind": "Component" }, { "spec.type": "website" } ] }

    空数组永远不匹配任何实体。

  • $exists:断言字段的存在性。其值为true(字段必须存在,无论值是什么)或false(字段必须不存在)。

    // 匹配没有该注解的实体,忽略其值可能是什么 { "metadata.annotations.backstage.io/orphan": { "$exists": false }, }
  • $in:断言字段具有一组原始值中的任意一个。其值必须是字符串、数字和/或布尔值组成的数组。

    // 匹配 type 为 service 或 website 的实体 { "spec.type": { "$in": ["service", "website"] } }

    匹配是大小写不敏感的。空数组永远不匹配任何实体。

  • $hasPrefix:断言字段是以某个前缀文本开头的字符串。其值是一个字符串。

    // 匹配 project slug 注解以 "backstage/" 开头的实体 { "metadata.annotations.github.com/project-slug": { "$hasPrefix": "backstage/" } }

    匹配大小写不敏感,且同时捕获精确匹配与以给定前缀开头的字符串。

  • $contains:断言数组包含一个匹配给定表达式的元素。此匹配器的支持有限。一个用例是关系:

    { // 仅支持 type 和(可选的)targetRef, // 且 targetRef 只支持相等或 "$in" "relations": { "$contains": { "type": "ownedBy", "targetRef": { "$in": ["user:default/foo", "group:default/bar"] } } } }

    另一个用例是元素为原始值的数组,例如标签:

    { // 适用于元素是原始值的数组字段 //(通常是字符串,数字和布尔值也受支持) "metadata.tags": { "$contains": "java" } }

值得补充的是,POST /entities/by-query的请求体(在 openapi.yaml 中定义)还支持与 GET 变体等价的cursorlimitoffsetorderByfield+order,其中order枚举为asc/desc)、fullTextFilterterm+fields)、fieldstotalItems等字段,谓词表达式则放在query字段中。

GET /entities—— 列出实体(已弃用)

列出实体。

注意:此端点已弃用,推荐使用GET /entities/by-query,后者提供了更高效的实现和基于游标的分页。

该端点支持以下查询参数:filter(过滤)、fields(字段选择)、offsetlimitafter(分页)。返回类型为 JSON 数组,元素为Entity

过滤

规则与by-query完全相同:多个过滤集合之间为 OR,集合内条件之间为 AND,条件为<key><key>=<value>形式,全部大小写不敏感;key 是简化 JSON 路径,遍历会深入数组;数组简单值项匹配为"键=项、值=true",关系可用relations.<type>=<targetRef>形式匹配。

同样的简例与真实示例同样适用(把路径换成/entities前缀即可):

  • 返回所有孤儿实体:

    /entities?filter=metadata.annotations.backstage.io/orphan=true

  • 返回所有用户和组:

    /entities?filter=kind=user&filter=kind=group

  • 返回所有 service 组件:

    /entities?filter=kind=component,spec.type=service

  • 返回所有带java标签的实体:

    /entities?filter=metadata.tags.java

  • 返回ops组的成员用户(注意使用组的完整引用):

    /entities?filter=kind=user,relations.memberof=group:default/ops

字段选择

by-query相同:fields参数为逗号分隔的简化 JSON 路径列表,保留指定的值或子树根,其余部分裁剪。

例如:

  • 只返回足以构成完整 ref 的数据:

    /entities?fields=kind,metadata.namespace,metadata.name

排序(Ordering)

默认情况下实体以未定义但稳定的顺序返回。可以传入一个或多个order查询参数来影响排序。

每个参数以asc:(升序字典序)或desc:(降序、反转字典序)开头,后跟实体键中的点分隔路径。排序大小写不敏感。如果给出了多个排序指令,后出现的指令优先级更低(仅在前面的指令值相等时才生效)。

示例:

/entities?order=asc:kind&order=desc:metadata.name

这会先按 kind 升序排列,然后在同 kind(如果某 kind 有多个实体)内部按名称降序排列。当给定字段在结果集内的某些实体上不存在时,无论期望顺序如何,缺少该字段的实体在该排序步骤中总是排到最后

分页(Pagination)

可以传入offsetlimit查询参数执行经典分页。此外还有after查询参数,用于在执行游标式分页时返回上一页之后的结果。

每个存在下一页数据的分页响应,都会携带一个Linkrel="next"头,指向下一页的查询路径。

示例——获取第一页:

GET /entities?limit=2 HTTP/1.1 200 OK link: </entities?limit=2&after=eyJsaW1pdCI6Miwib2Zmc2V0IjoyfQ%3D%3D>; rel="next" [{"metadata":{...

获取下一页(检测到Link头后继续):

GET /entities?limit=2&after=eyJsaW1pdCI6Miwib2Zmc2V0IjoyfQ%3D%3D HTTP/1.1 200 OK link: </entities?limit=2&after=eyJsaW1pdCI6Miwib2Zmc2V0Ijo0fQ%3D%3D>; rel="next" [{"metadata":{...

从源码层面看,GET /entities的实现值得一提:createRouter.ts 中,当未传入分页参数时,该接口实际会走流式响应路径——内部以每批 10000 条实体为上限循环调用entitiesCatalog.queryEntities,并通过createEntityArrayJsonStream将实体数组以流的形式写回客户端;只有当传入分页参数时,才会回退到"把所有实体加载进内存"的旧式慢路径。这解释了为何官方推荐改用by-query:即使在该端点内部,新实现也是基于queryEntities的。

GET /entities/by-uid/<uid>—— 按 UID 获取实体

根据实体的metadata.uid字段值获取单个实体。

返回类型为 JSON 的单个Entity,如果不存在该 UID 的实体则返回 404 错误。

DELETE /entities/by-uid/<uid>—— 按 UID 删除实体

根据实体的metadata.uid字段值删除单个实体。

注意:这种删除方式适用于孤儿实体(orphaned entities),但不适用于正在被某个 Location 活跃更新的"存活(live)"实体。请阅读下文说明。

最常见的用户流程是:注册一个 Location(见下文),然后目录会持续让自身与该 Location 及其可能衍生出的子树保持同步。这意味着目录是真实权威数据源的一个实时更新的视图。如果目录中有某种东西让实体保持"存活",那么用本节描述的方法删除后,它很快会再次出现。要彻底移除实体,通常应该转而注销(unregister)导致该实体出现的 Location

但如果你有一个孤儿实体——例如已经从某个Location实体中移除了对其文件的引用,或者某个处理器已停止产生你的实体——那么这种删除方法是合适的。

返回类型始终是空的 204 响应,无论该 UID 的实体是否存在。

GET /entities/by-name/<kind>/<namespace>/<name>—— 按引用三元组获取实体

根据实体的kindmetadata.namespacemetadata.name字段值获取实体。这三个字段很特殊,它们共同构成实体的唯一引用三元组。

返回类型为 JSON 的单个Entity,如果不存在该引用三元组的实体则返回 404。

GET /entities/by-name/{kind}/{namespace}/{name}/ancestry—— 获取实体谱系

按实体 ref 获取一个实体的谱系(ancestry)。在 openapi.yaml 中,该接口的响应 schema 为EntityAncestryResponse:包含rootEntityRef以及items数组,其中每个元素包含entityparentEntityRefs(父实体引用数组)。

POST /entities/by-refs—— 批量获取实体

按实体引用批量获取一组实体。这在需要高效获取大量特定实体的场景下很有用,例如在 GraphQL resolver 中。

请求体为如下形式的 JSON:

{ "entityRefs": ["component:default/foo", "api:default/bar"], "fields": ["kind", "metadata.name"] }

其中每个entityRefs条目都是你想获取的实体引用。fields数组是可选的,其作用与GET /entitiesfields相同,即只获取每个实体的某些切片。

返回类型为如下形式的 JSON:

{ "items": [{ "kind": "Component", "metadata": { "name": "foo" } }, null] }

其中items数组与输入的entityRefs数组长度相同、顺序一致。每个元素包含对应的实体数据;如果目录中不存在该 ref 对应的实体,则为null

在 openapi.yaml 中,该接口的响应 schema 为EntitiesBatchResponse,官方示例包括:按 refs 批量获取实体(entityRefs传入component:default/backstageapi:default/backstage),以及只获取实体的metadata.annotations。另外该端点还接受一个可选的filter查询参数。

POST /refresh—— 刷新实体

刷新与entityRef相关的实体。

请求体为如下形式的 JSON:

{ "entityRef": "<string>" }

在 createRouter.ts 中,该接口的实现调用了refreshService.refresh(...),并支持在请求体中以authorizationToken字段直接携带 token 进行认证(auth.authenticate);未携带时则回退到httpAuth.credentials(req)。请求成功后返回 200 空响应。同时,该路由在调用前后通过auditor服务记录entity-mutate审计事件,并带有queryType: 'refresh'entityRef元数据。

POST /validate-entity—— 校验实体

校验传入的实体在 schema 层面没有错误。

请求体为如下形式的 JSON:

{ "location": "<string>", "entity": {} }

校验失败时返回 400,响应体包含errors数组(每项含namemessage),成功时返回 200。在 createRouter.ts 中可以看到,创建 OpenAPI 路由时对该路径做了特殊处理(ignorePaths: /^\/validate-entity\/?$/),因为该校验接口的响应类型需要由路由实现而不是请求校验器来控制。该功能也作为独立的 scaffolder action 存在(createValidateEntityAction.ts)。

Locations(位置)接口

Location 是目录的数据源注册信息。它描述了一个目录数据应该从哪里摄取(例如一个指向catalog-info.yaml的 URL),以及该位置的类型(如url)。注册 Location 后,目录会持续从该来源同步数据并保持更新。

GET /locations—— 列出所有 Location

返回类型为如下形式的 JSON 数组:

[ { "data": { "id": "b9784c38-7118-472f-9e22-5638fc73bab0", "target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml", "type": "url" } } ]

在 openapi.yaml 中,Location对象除了targettypeid之外,还包含一个可选的entityRef字段,即对应 Location 类型实体的实体引用(例如location:default/generated-<sha1hex>)。

GET /locations/{id}—— 按 ID 获取 Location

按 Location ID 获取单个位置。

返回类型为如下形式的 JSON:

{ "id": "b9784c38-7118-472f-9e22-5638fc73bab0", "target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml", "type": "url" }

该端点还支持PUT /locations/{id}更新已有位置的 type 与 target(见 openapi.yaml)。

GET /locations/by-entity/{kind}/{namespace}/{name}—— 按实体获取 Location

获取引用给定实体的 Location。

返回类型为如下形式的 JSON:

{ "id": "b9784c38-7118-472f-9e22-5638fc73bab0", "target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml", "type": "url" }

GET /entity-facets?facet=<string>&facet=<string>&filter=<string>&filter=<string>—— 实体刻面统计

获取与给定过滤器匹配的所有实体刻面(facets)。

返回类型为如下形式的 JSON:

{ "facets": [ { "value": "<string>", "count": 1 } ] }

在 openapi.yaml 中,facet是必填的数组参数,官方示例包括kind(按 kind 统计实体数)与spec.type(按 spec type 统计)。响应 schemaEntityFacetsResponse中,facets是一个以刻面名为键、值为{value, count}数组的对象。该端点还提供POST变体QueryEntityFacetsByPredicate,可在请求体的query字段中使用谓词表达式进行过滤。

POST /locations—— 添加 Location

添加一个由目录摄取的 Location。

如果成功,响应码为HTTP/1.1 201 Created,响应 JSON 形式如下:

{ "entities": [], "location": { "id": "b9784c38-7118-472f-9e22-5638fc73bab0", "target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml", "type": "url" } }

如果该 Location 已经存在,响应为HTTP/1.1 409 Conflict,响应 JSON 形式如下:

{ "error": { "message": "Location url:https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml already exists", "name": "ConflictError", "stack": "ConflictError: Location url:https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml already exists\n..." }, "request": { "method": "POST", "url": "/locations" }, "response": { "statusCode": 409 } }

该端点支持以下查询参数(详见 openapi.yaml):

  • ?dryRun=true:执行校验但不向数据库写入任何内容。如果成功通过校验,响应 JSON 的entities字段会填充该位置中存在的实体;
  • ?onConflict=refresh|reject:控制位置已存在时的行为。reject(默认)返回 409 错误;refresh会触发对已有 Location 实体的刷新并返回 201。

请求体为{ "type": ..., "target": ... }LocationInput),其中targettype均必填。

POST /analyze-location—— 分析位置

校验给定的 Location。

请求体为如下形式的 JSON:

{ "location": { "type": "<string>", "target": "<string>" }, "catalogFileName": "<string>" }

响应类型为如下形式的 JSON:

{ "generateEntities": [ { "fields": [ { "description": "<string>", "value": "<string>", "state": "needsUserInput", "field": "<string>" }, { "description": "<string>", "value": {}, "state": "analysisSuggestedNoValue", "field": "<string>" } ], "entity": {} } ], "existingEntityFiles": [ { "entity": "<Entity>", "isRegistered": "<boolean>", "location": { "target": "<string>", "type": "<string>" } } ] }

从 openapi.yaml 的 schema 可以看出:

  • AnalyzeLocationEntityFieldstate枚举为analysisSuggestedValueanalysisSuggestedNoValueneedsUserInput,描述了对某个字段的分析结果(例如field可能是spec.owner,供前端在用户需要修改时把字段重新注入实体);
  • AnalyzeLocationExistingEntity描述的是:如果目标文件夹已包含 catalog info YAML 文件,它们会被读取并以该形式输出,使前端能告知用户已定位到这些文件,并在它们尚未注册时确保一并注册。

该接口通常被目录导入流程(catalog import)前端用来在注册前进行分析、预填充表单字段。

DELETE /locations/{id}—— 删除 Location

按 ID 删除 Location。成功时响应码为HTTP/1.1 204 No Content。正如"删除实体"一节所述,这通常是移除目录中"存活"实体的正确方式——删除导致实体出现的 Location,实体才会随之消失而不会重新出现。

源码视角:路由与实现佐证

如果你希望进一步深入这套 API 的底层实现,仓库中的关键证据包括:

  • openapi.yaml:目录 API 的完整 OpenAPI 3.1 规范,包含全部路径、参数(kindnamespacenameuidcursorafterfieldsfilteroffsetlimitorderFieldtotalItems)、请求体与响应 schema(EntityEntityRelationEntitiesQueryResponseLocationsQueryResponseEntityAncestryResponseEntitiesBatchResponseEntityFacetsResponseAnalyzeLocationResponse等)。
  • createRouter.ts:目录路由的组装入口。它基于createOpenApiRouter生成带请求校验的路由;实现要点包括:catalog.readonly只读模式(L115-L119)、POST /refresh的认证与审计事件(L122-L151)、GET /entities的流式响应与回退慢路径(L154-L247),以及对validate-entity的校验豁免(L94-L99)。
  • 配套的请求解析与响应写入工具,如service/request/parseEntityFilterParamsparseEntityQueryparseEntityOrderParamsparseEntityPaginationParams,以及service/response中的writeEntitiesResponse等,实现了文档中描述的过滤、排序、分页与字段裁剪语义。

相关文档

  • Entity References(实体引用格式):理解targetRefrelations.<type>=<targetRef>过滤以及by-name/by-refs接口中的引用语义。
  • Entity Descriptor Format(实体描述格式):Entity对象的结构定义(kindmetadataspecrelations)。
  • The Life of an Entity(实体的生命周期):理解"最终实体"与"原始摄取数据"的区别,以及处理与拼接流程。
  • 软件目录文档索引:目录功能的完整文档入口。

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 1:20:04

企业级代码生成选型:补全/重构/Agent三层架构与Bedrock实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 1:19:53

T_MATS安装实战:MATLAB/Simulink燃气轮机建模入门与避坑指南

简介&#xff1a;T_MATS是一款基于MATLAB的热力学系统建模与分析开源工具箱&#xff0c;专为燃气轮机、涡扇发动机等复杂热力系统仿真设计&#xff0c;面向航空工程领域的工程师、科研人员及高年级学生&#xff0c;适用于教学演示与课题研究。安装包共含400个文件&#xff0c;以…

作者头像 李华
网站建设 2026/9/10 1:17:56

8款降AI率工具实测:毕业论文AIGC检测如何从67%降到20%?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 1:14:52

安卓漫画APP选择与调优:正版平台与开源阅读器的实用指南

1. 选漫画APP前必须想明白的几件事 1.1 先分清“正版平台”和“第三方阅读器” 经常有朋友问我&#xff1a;“安卓手机上到底哪个漫画APP最好用&#xff1f;”说实话&#xff0c;这个问题如果不加前提&#xff0c;根本没法回答。因为市面上的漫画阅读工具大致分成两类&#xf…

作者头像 李华