mcpField
U
umbraco-openapi-client

umbraco-openapi-client

github.com/umbraco开发27397 次安装6 周前更新
代码仓库

关于 umbraco-openapi-client

为 Umbraco 后台扩展开发提供 OpenAPI 客户端实现指南,面向需要从前端调用自定义 API 的开发者。内容涵盖生成 TypeScript 客户端及配置 Umbraco 认证上下文,以处理令牌认证和类型安全调用。

主要功能

  1. 01生成 TypeScript OpenAPI 客户端
  2. 02配置 Umbraco bearer token 认证
  3. 03从 C# 模型生成类型
  4. 04说明客户端使用与认证流程
  5. 05列出常见认证错误

适用场景

  1. 01从后台前端调用自定义 API
  2. 02为树或工作区加载 API 数据
  3. 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 个部分:

  1. C# 后端:带有 Swagger/OpenAPI 文档的控制器
  2. 客户端依赖项:@hey-api/openapi-ts(其中已捆绑 @hey-api/client-fetch 插件)
  3. 生成脚本:获取 swagger.json 并生成 TypeScript 客户端
  4. 入口点配置:使用 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 },
});

生成工作流

  1. 启动 Umbraco——必须能够访问 swagger.json 端点
  2. 运行生成命令:npm run generate-client
  3. 生成的文件会出现在 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 的树

需要创建的关键文件

  1. Composers/MyApiComposer.cs——Swagger 注册
  2. Client/scripts/generate-openapi.js——生成脚本
  3. Client/src/entrypoints/entrypoint.ts——身份验证配置
  4. Client/src/api/——生成的文件(请勿手动编辑)

就是这样!务必生成你的 API 客户端并使用身份验证进行配置。绝不要对需要身份验证的端点使用原始 fetch。

评论

支持 Markdown