
Backstage Actions Service 實戰指南Action 的發現、過濾、鑒權與遠程調用原理【免費下載鏈接】backstageBackstage is an open framework for building developer portals項目地址: https://gitcode.com/GitHub_Trending/ba/backstageActions Service 是 Backstage 后端系統Backend System中的一項核心服務alpha 階段它為后端插件提供了一套統一的接口用于**發現list與執行invoke**已注冊的 Action。本文圍繞docs/backend-system/core-services/actions.md展開結合倉庫中 ActionsService 接口定義、DefaultActionsService 實現 與 actionsServiceFactory 測試講清 Action 的 ID 規范、pluginSources與 include/exclude 過濾配置、權限集成、Secrets 傳遞機制以及它在多插件、分布式場景下的底層遠程調用鏈路。讀完你可以在自己的插件中安全、精準地列出并執行 Action并掌握如何通過配置對暴露的 Action 做細粒度治理。Actions Service 是什么Actions Service 是 Backstage 后端插件中用于發現與執行已注冊 Action的核心服務。它承擔的是消費方 API角色Action 由插件通過 Actions Registry Service 注冊而本服務負責把這些 Action 暴露給其他插件與調用方并且自帶身份認證credentials與輸入校驗能力。在 ActionsService 接口定義 中服務只提供兩個方法list({ credentials })返回全部可用 Action 及其完整元數據ActionsServiceAction[]包括聲明的輸入/輸出/Secrets JSON Schema 與行為屬性attributesinvoke({ id, input, secrets, credentials })按 Action ID 執行指定的 Action返回{ output }。兩者都需要傳入BackstageCredentials用于認證與鑒權。接口定義中可以看到每個 Action 的元數據結構ActionsServiceActionexport type ActionsServiceAction { id: string; pluginId: string; name: string; title: string; description: string; schema: { input: JSONSchema7; output: JSONSchema7; secrets?: JSONSchema7; }; examples?: Array{ title: string; description?: string; input: JsonObject; output?: JsonObject; }; attributes: { readOnly: boolean; destructive: boolean; idempotent: boolean; }; };注意schema中的input/output/secrets在注冊時使用 Zod schema 描述但在經服務對外暴露時統一轉換為 JSON Schemadraft-07格式這一點在 actionsServiceFactory.test.ts 的集成測試斷言中可以看到具體形態。Action 的 ID 規范Action 使用全局唯一的 ID 標識格式固定為pluginId:actionName所有 Action ID 都以注冊它的插件 ID為前綴例如catalog插件注冊的fetch-user-info其完整 ID 為catalog:fetch-user-info使用actionsRegistryServiceMock測試 Mock注冊時插件前綴固定為test:這種命名約定保證了 Action 名稱在全部插件之間全局唯一同時讓每個 Action 的歸屬插件一目了然。從實現看ID 前綴還承擔了路由定位職責DefaultActionsService.invoke()會通過pluginIdFromActionId()解析出:之前的插件 IDDefaultActionsService.ts然后只向該插件發起遠程調用如果 ID 中沒有:會直接拋出Invalid action id錯誤。配置 Actions ServiceActions Service 的默認實現DefaultActionsService通過createServiceFactory裝配依賴discovery、rootConfig、logger、auth四個核心服務見 actionsServiceFactory.ts并讀取backend.actions配置段。以下配置都在app-config.yaml的backend.actions下完成。按插件限制 Action 來源pluginSourcespluginSources配置用于限制哪些插件的 Action 會被納入發現范圍backend: actions: pluginSources: - catalog實現上list()會讀取backend.actions.pluginSources字符串數組未配置時默認為空數組然后對每個來源插件發起 HTTP 請求獲取其 Action 列表DefaultActionsService.ts。某插件請求失敗時只會記錄warn日志并返回空數組不會讓整個list()失敗——這一優雅降級行為在測試should list all plugins in config to find actions and handle failures gracefully中有明確覆蓋actionsServiceFactory.test.ts。用 include / exclude 過濾 Action除了插件級限制Actions Service 還支持基于include包含與exclude排除規則的細粒度過濾精確控制 Backstage 實例中暴露或可運行的 Action。過濾維度有兩個id使用 glob 模式如catalog:*、*:fetch-*attributes按行為屬性過濾可取destructive破壞性、readOnly只讀、idempotent冪等三個布爾值。規則求值邏輯來自文檔原文并在 applyFilters 實現中得到印證單條規則內部id與attributes之間是AND關系matchesRule()中id 不匹配直接返回 falseattributes 有任一不一致也返回 false同一個include或exclude數組內的多條規則之間是OR關系exclude優先于include且始終生效先判 exclude命中即剔除無 include 規則時默認全量放行有 include 規則時至少命中一條才保留。包含指定 Actionbackend: actions: filter: include: # Include all catalog actions that are non-destructive - id: catalog:* attributes: destructive: false # OR include all fetch actions from any plugin - id: *:fetch-*排除指定 Actionbackend: actions: filter: exclude: # Exclude all delete actions from any plugin - id: *:delete-* # OR exclude all destructive actions - attributes: destructive: true上述語義在 actionsServiceFactory.test.ts 中有成體系的測試覆蓋可作為理解源碼級真相的參考should filter actions based on include patterns第 118-190 行include 只保留my-plugin:*should filter actions based on exclude patterns第 192-242 行exclude*:delete-*后只保留get-entityshould have exclude take precedence over include第 244-295 行即使命中 include被 exclude 命中也會被剔除should always apply exclude rules even when action matches include第 297-363 行注釋明確寫到 exclude is checked FIRST and always wins——destructive 的 action 即便匹配my-plugin:*也會被過濾should filter actions based on attribute constraints第 365-431 行按attributes.readOnly: true過濾should combine pattern and attribute filtering with AND logic第 433-525 行id與attributes同時滿足才保留should return all actions when no filter config is provided第 527-578 行未配置 filter 時返回全部。實現細節上glob 匹配使用minimatch庫規則解析見 parseFilterRulesid編譯為Minimatch實例attributes只讀取destructive、readOnly、idempotent三個鍵。與權限框架集成Permissions注冊時帶visibilityPermission字段的 Action 會自動接入權限框架列出時被權限策略permission policy拒絕的 Action 會從list()結果中過濾掉執行時對被拒絕的 Action 調用invoke()返回404 Not Found錯誤——與被刪除的 Action 表現一致避免暴露存在性信息。關于如何在 Action 上配置權限例如用createPermission定義my-plugin.actions.deleteEntity并賦值給visibilityPermission參見 Actions Registry 的 Permissions 文檔。使用 Actions Service 列出 Actionlist()返回的每個 Action 都帶有id、title、description、attributes以及可選的schema.input/schema.output/schema.secrets。下面是一個完整的列出示例沿用文檔原文可直接作為插件代碼參考import { ActionsService } from backstage/backend-plugin-api; export async function listAvailableActions( actionsService: ActionsService, credentials: BackstageCredentials, ) { try { const { actions } await actionsService.list({ credentials }); console.log(Found ${actions.length} available actions:); actions.forEach(action { console.log(- ${action.id}: ${action.title}); console.log( Description: ${action.description}); console.log( Attributes: ${JSON.stringify(action.attributes)}); if (action.schema.input) { console.log( Input Schema: ${JSON.stringify(action.schema.input, null, 2)}, ); } }); return actions; } catch (error) { console.error(Failed to list actions:, error); throw error; } }注意credentials參數服務內部會用auth.getPluginRequestToken({ onBehalfOf: credentials, targetPluginId })換取調用目標插件的服務令牌再攜帶Authorization: Bearer token請求遠程插件DefaultActionsService.ts。因此調用方必須提供代表當前用戶或服務的憑證不能匿名調用。執行一個 Actioninvoke()以id定位 Action并傳入input可選與secrets可選import { ActionsService } from backstage/backend-plugin-api; export async function executeAction( actionsService: ActionsService, actionId: string, input: JsonObject, credentials: BackstageCredentials, secrets?: JsonObject, ) { try { const { output } await actionsService.invoke({ id: actionId, input, secrets, credentials, }); console.log(Action ${actionId} executed successfully); console.log(Output:, JSON.stringify(output, null, 2)); return output; } catch (error) { console.error(Failed to execute action ${actionId}:, error); throw error; } } // Example usage async function fetchUserInfo( actionsService: ActionsService, credentials: BackstageCredentials, ) { const output await executeAction( actionsService, catalog:fetch-user-info, // Note: Action ID includes plugin prefix { userRef: user:default/john.doe, includeGroups: true, }, credentials, ); return output; }從實現看invoke()會根據目標插件 ID 把請求 POST 到該插件的/.backstage/actions/v1/actions/編碼后的完整ID/invoke端點DefaultActionsService.ts。測試should invoke the action and return the output與集成測試/api/test-harness/invoke分別驗證了客戶端 HTTP 調用與服務端全鏈路返回{ output: { ok: true, string: hello world } }的行為actionsServiceFactory.test.ts。錯誤語義invoke()對 HTTP 非 2xx 響應會統一轉為ResponseError拋出測試覆蓋了兩類典型場景目標 Action 不存在或權限被拒 →404should throw a 404 if the action does not exist輸入校驗失敗 →400should throw a 400 if the action returns an invalid input。攜帶 Secrets 執行 Action部分 Action 會為外部憑證如 GitHub Token、個人訪問令牌等不屬于 Backstage 自身認證體系的敏感值聲明secretsschema。你可以通過list()返回元數據中的schema.secrets判斷某 Action 是否需要 Secrets需要時在調用時一并傳入const { actions } await actionsService.list({ credentials }); const action actions.find(a a.id my-plugin:create-issue); if (action?.schema.secrets) { // This action needs secrets — collect them from the user first const { output } await actionsService.invoke({ id: action.id, input: { repo: backstage/backstage, title: My issue }, secrets: { githubToken: collectedToken }, credentials, }); }兩條硬性約束違反都會得到InputError向未聲明secrets schema 的 Action 傳入 secrets → 拒絕遺漏必需的 secrets → 拒絕。實現層面invoke()在傳入secrets時會自動切換到v2調用協議請求體為{ input, secrets }未傳 secrets 時保持v1協議請求體為input本身并在代碼中標注了待所有 registry 升級后移除 v1 回退的棄用說明DefaultActionsService.ts服務端則由 Actions Registry 在/.backstage/actions/v1/actions/:actionId/invoke舊與/.backstage/actions/v2/actions/:actionId/invoke新支持 wrapped secrets兩條路由上分別處理。關于如何在注冊端聲明 secrets schemaschema.secrets: z z.object({...})參見 Actions Registry 的 Secrets 文檔。特別地secrets 與 input 分離的設計保證了它們永遠不會出現在暴露為 MCP 工具的 tool definitions 或 LLM 上下文中。源碼視角一次 list/invoke 的完整鏈路把文檔描述與實現代碼對照可以還原 Actions Service 的完整工作鏈路注冊端插件在registerInit中注入actionsRegistryServiceRef服務 ref ID 為alpha.core.actionsRegistry見 refs.ts調用actionsRegistry.register({...})注冊 Action并掛載/.backstage/actions/v1/actions與 invoke 路由見 DefaultActionsRegistryService.ts 中的路由注冊重復注冊同一 ID 會拋出Action with id ... is already registered消費端調用方注入actionsServiceRefref ID 為alpha.core.actions見 refs.ts調用list()/invoke()路由與認證DefaultActionsService通過discovery.getBaseUrl(pluginId)定位目標插件用auth.getPluginRequestToken({ onBehalfOf: credentials, targetPluginId })換取服務令牌向{baseUrl}/.backstage/actions/v1/actionslist或/.backstage/actions/v1|v2/actions/{id}/invokeinvoke發起帶 Bearer 令牌的請求過濾與返回list()匯總各插件返回的 Action 后經applyFilters()應用 include/exclude 規則exclude 優先、include 缺省放行最終返回過濾后的列表。這套設計使 Actions Service 成為一個分布式的、跨插件的遠程調用門面Action 注冊在各自插件進程內但可以通過統一的服務接口被任意插件發現和調用且天然帶認證、鑒權與輸入校驗。最佳實踐與錯誤處理指引Action 設計規范命名、schema 設計等最佳實踐見 Actions Registry 的 Best Practices——Action 名應使用 kebab-case、以動詞開頭如fetch、create、delete、避免在名稱中重復插件名錯誤類型Action 內部應拋出backstage/errors中的錯誤類如NotFoundError、NotAllowedError這些錯誤能被 Actions Service 及 MCP Actions Backend 等消費方識別并透傳給調用者未被識別的錯誤類型可能退化為通用的500 Server Error。小結Actions Service 是 Backstage 后端系統中連接Action 注冊方與Action 消費方的橋梁pluginSources劃定插件邊界include/exclude 過濾提供基于 IDglob與行為屬性的細粒度治理visibilityPermission打通權限框架secrets 機制安全傳遞外部憑證而底層基于 Discovery Auth 的遠程調用讓 Action 可以跨插件分布式執行。配合 Actions Registry Service 一起使用即可在 Backstage 中構建一套可發現、可治理、可安全執行的可復用 Action 生態。【免費下載鏈接】backstageBackstage is an open framework for building developer portals項目地址: https://gitcode.com/GitHub_Trending/ba/backstage創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考