关于 umbraco-openapi-client
为 Umbraco 后台扩展开发提供 OpenAPI 客户端实现指南,面向需要从前端调用自定义 API 的开发者。内容涵盖生成 TypeScript 客户端及配置 Umbraco 认证上下文,以处理令牌认证和类型安全调用。
主要功能
- 01生成 TypeScript OpenAPI 客户端
- 02配置 Umbraco bearer token 认证
- 03从 C# 模型生成类型
- 04说明客户端使用与认证流程
- 05列出常见认证错误
适用场景
- 01从后台前端调用自定义 API
- 02为树或工作区加载 API 数据
- 03为自定义控制器生成客户端
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ umbraco/ umbraco-cms-backoffice-skills --skill umbraco-openapi-client安装后在 Claude Code 中输入/umbraco-openapi-client确认已加载。
详细介绍
Umbraco OpenAPI 客户端设置
关键:为何这很重要
绝不要使用原始的 fetch() 调用与 Umbraco 后台 API 通信。 原始的 fetch 调用会导致 401 Unauthorized 错误,因为它们不会包含 Umbraco 所需的 bearer token 身份验证。
务必使用配置了 Umbraco 身份验证上下文的生成式 OpenAPI 客户端。 这样可以确保:
- 正确进行 bearer token 身份验证
- API 调用具有类型安全性
- 自动处理 token 刷新
适用场景
在以下情况下使用此模式:
- 使用
[BackOfficeRoute]创建自定义 C# API 控制器 - 需要从后台前端调用自定义 API
- 构建树、工作区或任何从自定义端点加载数据的 UI
设置概览
设置分为 4 个部分:
- C# 后端:带有 Swagger/OpenAPI 文档的控制器
- 客户端依赖项:
@hey-api/openapi-ts(其中已捆绑@hey-api/client-fetch插件) - 生成脚本:获取 swagger.json 并生成 TypeScript 客户端
- 入口点配置:使用 Umbraco 身份验证配置客户端
分步实施
1. C# 后端设置(Swagger/OpenAPI)
你的 API 必须作为后台 OpenAPI 文档公开。创建一个 composer:
Umbraco 18+:OpenAPI 技术栈不再使用 Swashbuckle 的
SwaggerGenOptions(以及BackOfficeSecurityRequirementsOperationFilterBase/OperationIdHandler类型),改为使用流式的AddBackOfficeOpenApiDocument(...)构建器。请使用 以下模式;旧的SwaggerGenOptions方式在 v18 上已无法编译。
// Composers/MyApiComposer.cs
using Umbraco.Cms.Api.Common.OpenApi;
using Umbraco.Cms.Api.Management.OpenApi;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
namespace MyExtension.Composers;
public class MyApiComposer : IComposer
{
public void Compose(IUmbracoBuilder builder) =>
// Registers a dedicated backoffice OpenAPI document, served at
// /umbraco/openapi/{ApiName}.json and browsable via Swagger UI.
// See https://docs.umbraco.com/umbraco-cms/extend-your-project/tutorials/creating-a-backoffice-api
builder.AddBackOfficeOpenApiDocument(
Constants.ApiName,
document => document
.WithTitle("My Extension API")
.WithBackOfficeAuthentication()
.WithJsonOptions(Umbraco.Cms.Core.Constants.JsonOptionsNames.BackOffice)
.ConfigureOpenApiOptions(options =>
options.AddDocumentTransformer((doc, _, _) =>
{
doc.Info.Version = "1.0";
return Task.CompletedTask;
})));
}
// Constants
public static class Constants
{
public const string ApiName = "myextension";
}
2. 客户端 package.json 依赖项
将以下内容添加到你的 Client/package.json:
{
"scripts": {
"generate-client": "node scripts/generate-openapi.js https://localhost:44325/umbraco/openapi/myextension.json"
},
"devDependencies": {
"@hey-api/openapi-ts": "^0.97.0",
"chalk": "^5.4.1",
"node-fetch": "^3.3.2"
}
}
3. 生成脚本
创建 Client/scripts/generate-openapi.js:
import fetch from "node-fetch";
import chalk from "chalk";
import { createClient, defaultPlugins } from "@hey-api/openapi-ts";
console.log(chalk.green("Generating OpenAPI client..."));
const swaggerUrl = process.argv[2];
if (swaggerUrl === undefined) {
console.error(chalk.red(`ERROR: Missing URL to OpenAPI spec`));
process.exit(1);
}
// Ignore self-signed certificates on localhost
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
console.log(`Fetching OpenAPI definition from ${chalk.yellow(swaggerUrl)}`);
fetch(swaggerUrl)
.then(async (response) => {
if (!response.ok) {
console.error(chalk.red(`ERROR: ${response.status} ${response.statusText}`));
return;
}
await createClient({
input: swaggerUrl,
output: "src/api",
plugins: [
...defaultPlugins,
{
name: "@hey-api/client-fetch",
bundle: true,
exportFromIndex: true,
throwOnError: true,
},
{
name: "@hey-api/typescript",
enums: "typescript",
},
{
name: "@hey-api/sdk",
asClass: true,
},
],
});
console.log(chalk.green("Client generated successfully!"));
})
.catch((error) => {
console.error(`ERROR: ${chalk.red(error.message)}`);
});
4. 入口点配置(关键)
在入口点中使用 Umbraco 身份验证上下文配置生成的客户端:
// src/entrypoints/entrypoint.ts
import type { UmbEntryPointOnInit, UmbEntryPointOnUnload } from "@umbraco-cms/backoffice/extension-api";
import { UMB_AUTH_CONTEXT } from "@umbraco-cms/backoffice/auth";
import { client } from "../api/client.gen.js";
export const onInit: UmbEntryPointOnInit = (host, _extensionRegistry) => {
// CRITICAL: Configure the OpenAPI client with authentication
host.consumeContext(UMB_AUTH_CONTEXT, (authContext) => {
if (!authContext) return;
// configureClient() sets baseUrl/credentials/bearer token AND binds the
// default interceptors (401 retry, problem-details error notifications).
// It guards against configuring the same client twice, so it's safe to
// call for umbHttpClient and your own generated client alike.
authContext.configureClient(client);
console.log("API client configured with auth");
});
};
export const onUnload: UmbEntryPointOnUnload = (_host, _extensionRegistry) => {
// Cleanup if needed
};
手动替代方案(跳过默认拦截器)
如果你需要完全控制客户端配置,且不想使用默认拦截器,可以改为手动配置客户端:
const config = authContext.getOpenApiConfiguration();
client.setConfig({
baseUrl: config.base,
credentials: config.credentials,
auth: config.token, // This provides the bearer token!
});
这种方式可以正确进行身份验证,但你将失去 configureClient() 所配置的 401 重试和 problem-details 错误处理,而且会与当前的 bearer-token 机制耦合,而不是将身份验证视为黑盒。
5. 使用生成的客户端
运行 npm run generate-client 后,使用生成的服务:
// In your workspace context, repository, or data source
import { MyExtensionService } from "../api/index.js";
// The client handles auth automatically!
const response = await MyExtensionService.getItems({
query: { skip: 0, take: 50 },
});
const item = await MyExtensionService.getItem({
path: { id: "some-guid" },
});
await MyExtensionService.createItem({
body: { name: "New Item", value: 123 },
});
生成工作流
- 启动 Umbraco——必须能够访问 swagger.json 端点
- 运行生成命令:
npm run generate-client - 生成的文件会出现在
src/api/中:types.gen.ts——根据你的 C# 模型生成的 TypeScript 类型sdk.gen.ts——带有类型化方法的服务类client.gen.ts——HTTP 客户端配置index.ts——重新导出所有内容
常见错误
❌ 错误:原始 fetch
// This will get 401 Unauthorized!
const response = await fetch('/umbraco/myextension/api/v1/items');
❌ 错误:仅使用 credentials 的 fetch
// Still fails - cookies don't work for Management API
const response = await fetch('/umbraco/myextension/api/v1/items', {
credentials: 'include'
});
✅ 正确:生成式 OpenAPI 客户端
// Client is configured with bearer token in entry point
const response = await MyExtensionService.getItems();
参考示例
请参阅以下完整可运行的实现:
examples/notes-wiki/Client/——完整的 OpenAPI 客户端设置examples/tree-example/Client/——集成 OpenAPI 的树
需要创建的关键文件
Composers/MyApiComposer.cs——Swagger 注册Client/scripts/generate-openapi.js——生成脚本Client/src/entrypoints/entrypoint.ts——身份验证配置Client/src/api/——生成的文件(请勿手动编辑)
就是这样!务必生成你的 API 客户端并使用身份验证进行配置。绝不要对需要身份验证的端点使用原始 fetch。