0

0

在EJS中正确渲染CKEditor生成的HTML内容

聖光之護

聖光之護

发布时间:2025-11-02 09:47:15

|

537人浏览过

|

来源于php中文网

原创

在ejs中正确渲染ckeditor生成的html内容

本文旨在解决在EJS模板中显示由CKEditor等富文本编辑器生成的HTML内容时,内容被默认转义为纯文本的问题。通过详细阐述EJS的HTML转义机制,并提供正确的解决方案——使用``而非``,确保富文本内容能以预期的格式渲染。同时,文章强调了在处理用户生成HTML内容时的安全考量,特别是跨站脚本攻击(XSS)的防范措施。

在现代Web应用开发中,富文本编辑器(如CKEditor)是创建博客、新闻或内容管理系统不可或缺的工具。它们允许用户以所见即所得的方式编辑内容,生成包含各种HTML标签(如<strong>、<em>、<p>等)的字符串。然而,当我们将这些HTML字符串存储到数据库并在Node.js/Express应用中使用EJS作为视图引擎进行渲染时,常常会遇到一个问题:内容以原始HTML字符串的形式显示,而不是浏览器解析后的样式。本教程将深入探讨这一问题的原因,并提供一个简洁而安全的解决方案。

理解EJS中的HTML转义机制

EJS(Embedded JavaScript)是一个流行的模板引擎,它默认会对输出的内容进行HTML转义。这意味着,当你在EJS模板中使用<%= variable %>语法来显示一个字符串时,所有潜在的HTML特殊字符(如<、>、&、"、')都会被转换成对应的HTML实体(如、&、"、')。

这种默认行为是出于安全考虑,主要是为了防止跨站脚本攻击(XSS)。如果用户在富文本编辑器中输入了恶意脚本(例如<script>alert('XSS!');</script>),并且这些脚本未经转义直接渲染到页面上,那么访问该页面的用户就会执行这些恶意代码。通过转义,这些脚本会被当作普通文本显示,从而避免了攻击。

立即学习前端免费学习笔记(深入)”;

问题示例:

假设你有一个Node.js/Express应用,使用CKEditor收集用户输入的博客内容,并将其存储为HTML字符串。在EJS模板中,你可能这样尝试显示它:

<!-- views/post.ejs -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>文章详情</title>
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    <div class="container">
        <h1><%= title %></h1>
        <div>
            <%= content %>
        </div>
    </div>
</body>
</html>

如果content变量包含以下HTML字符串:

<p><strong>Lorem ipsum</strong> dolor sit amet, consectetur adipisicing elit.<i> Quae maxime</i> dolore necessitatibus iste aliquid dolorum in nostrum repellat rerum atque?</p>

那么在浏览器中,你看到的结果将是:

<p><strong>Lorem ipsum</strong> dolor sit amet, consectetur adipisicing elit.<i> Quae maxime</i> dolore necessitatibus iste aliquid dolorum in nostrum repellat rerum atque?</p>

这显然不是我们想要的效果,因为HTML标签被当作普通文本显示,而不是被浏览器解析并应用样式。

解决方案:使用不转义的输出

EJS提供了一种特殊的语法来指示模板引擎不要对内容进行HTML转义,直接将其作为原始HTML渲染。这个语法就是<%- variable %>。

正确的使用方式:

将你的EJS模板中的内容输出语句从<%= content %>修改为<%- content %>:

<!-- views/post.ejs (修改后) -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>文章详情</title>
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    <div class="container">
        <h1><%= title %></h1>
        <div>
            <%- content %> <!-- 注意这里的区别 -->
        </div>
    </div>
</body>
</html>

通过这个简单的修改,当content变量包含HTML字符串时,EJS将不再对其进行转义,而是将其直接插入到HTML文档中。浏览器会解析这些HTML标签,并按照预期应用样式和结构。

预期输出效果:

灵机语音
灵机语音

灵机语音

下载

Lorem ipsum dolor sit amet, consectetur adipisicing elit.Quae maxime dolore necessitatibus iste aliquid dolorum in nostrum repellat rerum atque?

完整示例代码

为了更好地理解,我们提供一个包含CKEditor表单提交和EJS渲染的简化Node.js/Express应用示例。

1. Express应用 (app.js):

const express = require('express');
const bodyParser = require('body-parser');
const ejs = require('ejs');

const app = express();
const port = 3000;

app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public')); // 假设你的CKEditor和CSS文件在public目录下

// 存储文章内容的简单模拟
let posts = [];

// 首页 - 显示所有文章
app.get('/', (req, res) => {
    res.render('index', { posts: posts });
});

// 发布新文章的表单页面
app.get('/compose', (req, res) => {
    res.render('compose');
});

// 处理新文章提交
app.post('/compose', (req, res) => {
    const newPost = {
        title: req.body.postTitle,
        content: req.body.postBody // CKEditor生成的HTML内容
    };
    posts.push(newPost);
    res.redirect('/');
});

// 查看单篇文章页面
app.get('/posts/:id', (req, res) => {
    const postId = req.params.id;
    if (postId < posts.length) {
        res.render('post', {
            title: posts[postId].title,
            content: posts[postId].content
        });
    } else {
        res.status(404).send('文章未找到');
    }
});

app.listen(port, () => {
    console.log(`Server started on port ${port}`);
});

2. CKEditor表单页面 (views/compose.ejs):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>撰写新文章</title>
    <link rel="stylesheet" href="/css/styles.css">
    <!-- 引入CKEditor库 -->
    <script src="https://cdn.ckeditor.com/ckeditor5/41.4.2/classic/ckeditor.js"></script>
</head>
<body>
    <div class="container">
        <h1>撰写新文章</h1>
        <form action="/compose" method="post">
            <div class="form-group">
                <label for="postTitle">文章标题</label>
                <input type="text" class="form-control" id="postTitle" name="postTitle" required>
            </div>
            <div class="form-group">
                <label for="editor">文章内容</label>
                <textarea name="postBody" id="editor">在这里开始写作...</textarea>
            </div>
            <button type="submit" class="btn btn-primary">发布</button>
        </form>
    </div>

    <script>
        ClassicEditor
            .create(document.querySelector('#editor'))
            .then(editor => {
                console.log(editor);
            })
            .catch(error => {
                console.error(error);
            });
    </script>
</body>
</html>

3. 文章详情页面 (views/post.ejs):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title><%= title %></title>
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    <div class="container">
        <h1><%= title %></h1>
        <div class="post-content">
            <%- content %> <!-- 使用 <%- %> 来渲染CKEditor生成的HTML -->
        </div>
        <a href="/" class="btn btn-secondary">返回首页</a>
    </div>
</body>
</html>

重要注意事项与安全考量

虽然使用<%- variable %>可以解决HTML渲染问题,但这是一个潜在的安全风险点,尤其是在处理用户生成的内容时。

1. 跨站脚本攻击 (XSS) 风险: 如果你的应用允许用户提交任意HTML内容,并且你直接使用<%- content %>进行渲染,那么恶意用户就可以注入XSS攻击代码。例如,用户可以输入:

<p>我的文章</p><script>alert('您被攻击了!');</script>

如果这段内容未经处理直接渲染,那么访问该页面的其他用户就会看到一个弹窗,甚至更严重的攻击(如窃取Cookie、重定向到恶意网站等)。

2. 内容净化 (HTML Sanitization): 为了安全地显示用户生成的HTML内容,强烈建议在服务器端对内容进行净化(Sanitization)。净化是指移除HTML字符串中所有不安全或不必要的标签和属性,只保留安全的、允许的HTML结构。

常用的HTML净化库包括:

  • DOMPurify (Node.js版本): 一个非常强大和安全的HTML净化库,可以在服务器端使用。
  • js-xss: 另一个流行的HTML净化库,提供了高度可配置的白名单机制。

净化流程建议:

  1. 用户通过CKEditor提交内容。
  2. 在Express路由中接收到req.body.postBody后,不要直接存储。
  3. 使用HTML净化库对req.body.postBody进行处理,移除所有潜在的恶意内容。
  4. 将净化后的HTML内容存储到数据库。
  5. 从数据库读取内容后,在EJS模板中使用<%- content %>进行渲染。此时,由于内容已经过净化,可以相对安全地直接渲染。

示例(使用DOMPurify):

首先安装DOMPurify:

npm install dompurify jsdom

然后在Express路由中:

const express = require('express');
const bodyParser = require('body-parser');
const ejs = require('ejs');
const { JSDOM } = require('jsdom'); // 用于DOMPurify的JSDOM环境
const createDOMPurify = require('dompurify');

const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);

// ... 其他代码 ...

app.post('/compose', (req, res) => {
    const rawContent = req.body.postBody;
    // 净化HTML内容
    const cleanContent = DOMPurify.sanitize(rawContent, {
        USE_PROFILES: { html: true } // 允许标准的HTML标签
        // 可以根据需求配置允许的标签和属性
    });

    const newPost = {
        title: req.body.postTitle,
        content: cleanContent // 存储净化后的HTML内容
    };
    posts.push(newPost);
    res.redirect('/');
});

// ... 其他代码 ...

总结

在EJS模板中显示由CKEditor等富文本编辑器生成的HTML内容时,核心在于理解EJS的HTML转义机制。使用<%- variable %>语法可以确保HTML内容被正确解析和渲染。然而,为了构建一个安全可靠的Web应用,务必在服务器端对用户生成的所有HTML内容进行严格的净化处理,以防范潜在的XSS攻击。遵循“输入验证,输出净化”的原则,将有效提升应用的安全性。

相关文章

HTML速学教程(入门课程)
HTML速学教程(入门课程)

HTML怎么学习?HTML怎么入门?HTML在哪学?HTML怎么学才快?不用担心,这里为大家提供了HTML速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Node.js后端开发与Express框架实践
Node.js后端开发与Express框架实践

本专题针对初中级 Node.js 开发者,系统讲解如何使用 Express 框架搭建高性能后端服务。内容包括路由设计、中间件开发、数据库集成、API 安全与异常处理,以及 RESTful API 的设计与优化。通过实际项目演示,帮助开发者快速掌握 Node.js 后端开发流程。

434

2026.02.10

cookie
cookie

Cookie 是一种在用户计算机上存储小型文本文件的技术,用于在用户与网站进行交互时收集和存储有关用户的信息。当用户访问一个网站时,网站会将一个包含特定信息的 Cookie 文件发送到用户的浏览器,浏览器会将该 Cookie 存储在用户的计算机上。之后,当用户再次访问该网站时,浏览器会向服务器发送 Cookie,服务器可以根据 Cookie 中的信息来识别用户、跟踪用户行为等。

6500

2023.06.30

document.cookie获取不到怎么解决
document.cookie获取不到怎么解决

document.cookie获取不到的解决办法:1、浏览器的隐私设置;2、Same-origin policy;3、HTTPOnly Cookie;4、JavaScript代码错误;5、Cookie不存在或过期等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

368

2023.11.23

阻止所有cookie什么意思
阻止所有cookie什么意思

阻止所有cookie意味着在浏览器中禁止接受和存储网站发送的cookie。阻止所有cookie可能会影响许多网站的使用体验,因为许多网站使用cookie来提供个性化服务、存储用户信息或跟踪用户行为。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

447

2024.02.23

cookie与session的区别
cookie与session的区别

本专题整合了cookie与session的区别和使用方法等相关内容,阅读专题下面的文章了解更详细的内容。

97

2025.08.19

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

761

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1570

2023.10.24

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

69

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.9万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

CSS教程
CSS教程

共754课时 | 43.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号