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

聖光之護
发布: 2025-11-02 09:47:15
原创
496人浏览过

在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标签,并按照预期应用样式和结构。

预期输出效果:

Trae国内版
Trae国内版

国内首款AI原生IDE,专为中国开发者打造

Trae国内版815
查看详情 Trae国内版

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攻击。遵循“输入验证,输出净化”的原则,将有效提升应用的安全性。

以上就是在EJS中正确渲染CKEditor生成的HTML内容的详细内容,更多请关注php中文网其它相关文章!

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

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

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

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