在 ASP.NET Core 中通过 ZIP 包集成 CKEditor 5:完整实战指南
CKEditor 5 本质上是一个纯 JavaScript/TypeScript 的富文本编辑器库,没有任何官方 .NET 专属集成,因此它可以被部署到任何能托管静态资源的服务端环境——包括 Microsoft 的 .NET 平台。本文基于 .NET 集成文档 的完整流程,演示如何在 ASP.NET Core 应用中通过 ZIP 归档放置自定义编辑器构建,并通过 import map 与 ES Module 语法完成页面集成,最终用 dotnet watch run 启动应用。读完本文,你将掌握从项目搭建、静态资源托管、Razor 页面改造到 license key 配置的端到端实操能力。
集成方式总览:为什么选择 ZIP 自托管
把 CKEditor 5 放进 .NET 应用,主要有两种分发路径:
| 维度 | CDN(云托管) | ZIP 归档(自托管) |
|---|---|---|
| 资源来源 | cdn.ckeditor.com 全局分发 |
从 Customer Portal 下载并解压到 wwwroot |
| 托管成本 | 无需自建静态资源服务 | 需要自己托管静态文件 |
| 许可证 | 需商业许可,不支持 'GPL' 键 |
支持 GPL 或自托管商业许可 |
| 适合场景 | 快速接入、云端计费 | 内网/私有化部署、离线环境、灵活控制版本 |
采用 ZIP 自托管时,你从 Customer Portal 下载包含自定义构建的 ZIP 包(通常是使用 CKEditor 5 Builder 生成的、包含所选插件与工具栏的构建产物),将其中两个产物——ckeditor5.js 与 ckeditor5.css——复制到项目的 wwwroot/lib/ckeditor5/ 目录。由于没有官方 .NET 集成,应用只需负责把这些静态资源原样提供给浏览器,编辑器在浏览器端以 ES Module 方式加载并实例化。
自托管下使用商业构建,需要为自托管分发获得相应许可;GPL 合规场景可直接使用开源许可。具体授权约束见下文「license key 配置」小节。
创建 ASP.NET Core 项目
本文以 dotnet new webapp 创建的 ASP.NET Core Web 应用(Razor Pages 模板)为例:
dotnet new webapp
该命令会生成包含 Pages/、wwwroot/、appsettings.json 等目录的骨架项目。若需从零了解 ASP.NET Core 框架本身,可参考微软官方 ASP.NET Core 入门文档;本文后续步骤只需一个能正常返回页面并托管静态文件的项目即可。
下载 ZIP 包并复制静态资源
获取构建产物
在 CKEditor 5 Builder 中配置好所需插件与工具栏后下载 ZIP 归档。解压后,把其中两个文件复制到静态资源目录:
# 假设解压目录为 ./ckeditor5-dist
mkdir -p wwwroot/lib/ckeditor5
cp ./ckeditor5-dist/ckeditor5.js wwwroot/lib/ckeditor5/
cp ./ckeditor5-dist/ckeditor5.css wwwroot/lib/ckeditor5/
预期的目录结构
完成后,应用的文件夹结构应与下面类似:
├── bin
├── obj
├── Pages
│ ├── Index.cshtml
│ └── ...
├── Properties
├── wwwroot
│ ├── css
│ ├── js
│ ├── lib
│ │ ├── bootstrap
│ │ ├── ckeditor5
│ │ │ ├── ckeditor5.js
│ │ │ └── ckeditor5.css
│ │ ├── jquery
│ │ ├── jquery-validation
│ │ └── jquery-validation-unobtrusive
│ └── favicon.ico
├── appsettings.Development.json
├── appsettings.json
└── ...
关键点:ckeditor5.js 与 ckeditor5.css 必须处于同一目录(本例为 wwwroot/lib/ckeditor5/),因为 JS 文件内部会按相对路径解析其依赖的 CSS、图标等资源。
自托管版本的 license key 要求
从版本 44.0.0 起,licenseKey 配置项成为使用编辑器的必要条件。使用 ZIP 自托管版本时,必须满足以下二选一:
- 遵守 GPL:将
licenseKey配置为'GPL'; - 获取自托管分发的商业许可:配置你在 Customer Portal 中获得的商业 license key。
如果只是想评估自托管方案,可以在 CKEditor 5 Customer Portal 申请免费试用来测试编辑器并评估自托管分发。
改造 Razor 页面:Index.cshtml 完整模板
ZIP 包内的 index.html 已包含全部必要标记(import map、CSS 链接与模块化初始化脚本)。将其移植到 Pages/Index.cshtml 的 <script> 标签中即可,但务必注意 import map 与 CSS 链接的路径要与你的实际目录结构一致。以下是一份可直接套用的完整模板:
@page
@using Microsoft.AspNetCore.Components
@{
ViewData["Title"] = "Home Page";
var data = new ImportMapDefinition(
new Dictionary<string, string>
{
{ "ckeditor5", "/lib/ckeditor5/ckeditor5.js" },
{ "ckeditor5/", "/lib/ckeditor5/" },
}, null, null);
}
<link href="~/lib/ckeditor5/ckeditor5.css" rel="stylesheet" />
<div class="main-container">
<div id="editor">
<p>Hello from CKEditor 5!</p>
</div>
</div>
<script type="importmap" asp-importmap="@data"></script>
<script type="module">
import {
ClassicEditor,
Essentials,
Paragraph,
Bold,
Italic,
Font
} from 'ckeditor5';
ClassicEditor
.create( {
attachTo: document.querySelector( '#editor' ),
licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
plugins: [ Essentials, Paragraph, Bold, Italic, Font ],
toolbar: [
'undo', 'redo', '|', 'bold', 'italic', '|',
'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor'
]
} )
.then( editor => {
window.editor = editor;
} )
.catch( error => {
console.error( error );
} );
</script>
模板要点逐项解析
@using Microsoft.AspNetCore.Components与ImportMapDefinition:Razor 页面通过ImportMapDefinition在服务端构建浏览器原生 import map,把裸模块名ckeditor5映射到/lib/ckeditor5/ckeditor5.js,并把ckeditor5/前缀映射到/lib/ckeditor5/目录(用于解析内部相对依赖)。asp-importmap标签助手负责将该对象序列化为<script type="importmap">。- CSS 链接:
<link href="~/lib/ckeditor5/ckeditor5.css" rel="stylesheet" />中的~是 Razor 静态资源根路径语法,对应wwwroot目录。 - 编辑器容器:
<div id="editor">是编辑器的挂载点,其内部预置的<p>Hello from CKEditor 5!</p>会成为编辑器的初始内容。 - ES Module 导入:
<script type="module">中从ckeditor5(由 import map 解析)按命名方式导入ClassicEditor与各插件类。 .create()配置:attachTo:指定编辑器挂载的 DOM 元素(#editor);licenseKey:必填,商业 key 或'GPL';plugins:声明实际加载的插件,此处包含Essentials、Paragraph、Bold、Italic、Font;toolbar:工具栏按钮顺序,'|'表示分隔符,fontSize/fontFamily/fontColor/fontBackgroundColor均由Font插件提供。
- 异步生命周期:
create()返回 Promise,.then( editor => { window.editor = editor; } )把实例挂到全局便于调试;.catch()负责打印初始化错误。
注意:ZIP 构建里包含哪些插件,取决于你在 Builder 中选择的预设。上例中的插件与工具栏按钮仅对应文档示例,实际使用时应与你的构建产物保持一致,否则导入不存在的模块会报错。
处理 Chromium 的 import map 限制
由于 Chromium 内核的已知问题(chromium issue 40611854),浏览器目前不支持同时存在多个 import map。当前版本的 .NET Web 应用模板可能已经在共享布局中定义了一个 import map。如果你遇到这种情况:
- 打开
/Pages/Shared/__Layout.cshtml; - 删除其中已有的
<script type="importmap"></script>标签(或对应内容); - 刷新页面,编辑器即可正常加载。
删除共享布局中的 import map 不会影响应用其他功能,因为该 import map 在本集成中由 Index.cshtml 内的新 import map 完全接管。
启动应用
在 .NET 项目根目录执行:
dotnet watch run
dotnet watch run 会构建项目、启动 Kestrel,并监听源文件变化自动重启/刷新。浏览器访问应用主页后,即可在 #editor 容器中看到 CKEditor 5 编辑器实例。
licenseKey 配置的底层校验逻辑
了解编辑器端如何校验 licenseKey,有助于排查自托管集成中的授权问题。从 editor.ts 的源码可以看到,编辑器初始化时依次执行:
- 读取配置:
config.get( 'licenseKey' ); - 全局键兜底:若配置缺失,会检查
window.CKEDITOR_GLOBAL_LICENSE_KEY全局变量,存在则自动写入配置; - 缺失即报错:若最终仍无 license key,抛出
license-key-missing的CKEditorError,错误信息中明确提示:商业场景请提供 key,GPL 场景请使用'GPL'。
在 editorconfig.ts 中,licenseKey 被声明为 EditorConfig 的可选字符串属性。校验通过后,编辑器还会根据 key 类型(GPL / 商业 JWT 载荷)决定是否启用只读限制、显示 "Powered by CKEditor" 标识等行为(见 editor.ts 中 verifyLicenseKey 的实现)。
这一点对 ZIP 自托管尤其重要:如果页面里忘记写 licenseKey,即使资源路径全部正确,编辑器也会在初始化阶段直接抛错,而不会静默降级。
常见问题与排错清单
| 症状 | 排查方向 |
|---|---|
浏览器控制台报 license-key-missing |
确认 create() 配置中已写 licenseKey: '<YOUR_LICENSE_KEY>' 或 'GPL'(44.0.0 起强制要求) |
| 编辑器不渲染,模块导入 404 | 核对 wwwroot/lib/ckeditor5/ 下 ckeditor5.js、ckeditor5.css 是否存在,以及 import map 中 /lib/ckeditor5/... 路径是否与目录结构一致 |
| 出现多个 import map 相关报错 | 删除 Pages/Shared/__Layout.cshtml 中已有的 <script type="importmap"> 标签 |
| 工具栏按钮与导入的插件不匹配 | 确认 plugins 与 toolbar 项来自你在 Builder 中选定的构建预设 |
| 修改后页面不刷新 | 确认是用 dotnet watch run 启动,且改动的是受监听的文件 |
后续深入方向
- 学习如何读取、修改编辑器数据并监听变化:获取与设置数据;
- 进一步定制编辑器行为与配置项:配置指南;
- 了解工具栏按钮的完整定制方式:工具栏配置;
- 查看各功能模块的独立指南:功能总览;
- 若你更希望走 CDN 云分发路线(无需自托管静态资源),可参考同系列的 .NET CDN 集成,两种方式的页面骨架几乎一致,只是资源来源与许可要求不同。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.26 K641- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python860
SlideSCIPPT插件,支持素材库、AI助手、一键添加图片标题,复制粘贴位置、一键图片对齐、一键插入Markdown(加粗、超链接等行内样式、代码块、LaTeX等块级样式)、便捷导出图片!C#621
Agent-Reach给你的 AI Agent 一键装上互联网能力。13 个平台(网页/GitHub/YouTube/小红书/B站/Twitter/Reddit 等)多后端路由,当下最稳的接入方式替你选好、装好、体检好。GitHub 主仓库同步镜像。Python1304
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go23446
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java37451