如何使用Golang实现一个简易的Web端Markdown编辑器

2次阅读

go只需专注接收markdown、安全转html并返回,前端用轻量编辑器即可;goldmark需配置html.withunsafe()和自定义sanitizer白名单防xss接口须设json content-type、限流校验,前端防抖调用并直接渲染html。

如何使用Golang实现一个简易的Web端Markdown编辑器

为什么不用现成的前端编辑器(如 CodeMirror、Toast ui)?

因为你要的是「golang 实现」——重点在后端能力,不是前端渲染。真正需要 Go 做的只有三件事:接收 Markdown 文本、安全转为 HTML、返回给前端。前端编辑器用 textarea 或轻量级库(比如 simplemde)就够了,Go 只管「接、转、吐」。

常见错误是把整个编辑器逻辑塞进 Go:比如想用 Go 渲染实时预览、处理键盘快捷键、做语法高亮……这些该由浏览器完成,Go 做只会卡顿、难调试、跨域麻烦。

  • Go 不该处理 dom 操作或用户输入事件
  • 所有 HTML 转换必须在服务端完成,不能信前端传来的 __html 字段
  • 若用 blackfriday(已归档),注意它不维护了;推荐 goldmark,默认禁用 XSS,更省心

goldmark 渲染 Markdown 时怎么防 XSS?

goldmark 默认不开 HTML 标签,但如果你允许用户粘贴含 <script></script> 的内容,又没配好选项,照样中招。关键不是“关不关 HTML”,而是「明确声明哪些标签/属性可保留」。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • goldmark.WithExtensions() 启用基础扩展(表格、脚注等),但别加 goldmark.Extender 自定义危险节点
  • 必须显式配置 html.WithUnsafe() —— 听起来反直觉,但这是「启用 HTML 解析」的开关;没它,<div> 会被原样转义 <li>真正防 XSS 靠 <code>html.WithSanitizer(),传入自定义函数,只放行 <p></p><pre class="brush:php;toolbar:false;">&lt;/code&gt;、&lt;code&gt;&lt;code&gt;&lt;/code&gt;、&lt;code&gt;&lt;strong&gt;&lt;/code&gt; 等白名单标签&lt;/li&gt; &lt;li&gt;别用 &lt;code&gt;bluemonday&lt;/code&gt; 二次过滤——goldmark 自带 sanitizer 更轻、更可控&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;示例关键片段:&lt;/p&gt; &lt;pre class='brush:php;toolbar:false;'&gt;md := goldmark.New( goldmark.WithExtensions(extension.GFM), goldmark.WithRendererOptions( html.WithUnsafe(), // 允许解析原始 HTML html.WithSanitizer(func(tag string, attrs []html.Attribute) bool { allowed := map[string]bool{&quot;p&quot;: true, &quot;code&quot;: true, &quot;pre&quot;: true, &quot;strong&quot;: true, &quot;em&quot;: true} return allowed[tag] }), ), )</pre> <h3>POST 接口接收 Markdown 内容要注意什么?</h3> <p>很多人写 <code>http.HandleFunc("/render", ...),直接读 r.Body,结果中文乱码、换行丢失、超长文本截断——根本原因是没设 Content-Type 和读取方式。

    正确做法:

    • 前端必须发 Content-Type: application/json,字段名统一用 content(别用 markdowntext,易和 Form 表单混淆)
    • Go 后端用 json.NewDecoder(r.Body).Decode(&req),而不是 r.FormValue("content") —— 后者只适用于 application/x-www-form-urlencoded
    • http.MaxBytesReader 限制请求体大小,比如 1MB:http.MaxBytesReader(w, r, 1024*1024),否则恶意大文件会耗尽内存
    • 别在 handler 里做复杂渲染——先校验长度(如 len(req.Content) ),再交给 <code>goldmark 处理

    前端怎么和 Go 渲染接口配合最省事?

    别写一 ajax 状态管理。一个 textarea + 一个 div#preview + 一个防抖提交就够了。

    要点:

    • inputkeyup 事件监听编辑区,但加 300ms 防抖,避免每敲字都发请求
    • 请求用 fetch("/render", { method: "POST", body: JSON.stringify({content: text}) }),别漏 headers: {"Content-Type": "application/json"}
    • 响应直接赋值给 preview.innerHTML —— 因为 goldmark 输出的是安全 HTML,不用再 DOMPurify.sanitize()
    • 如果预览区样式错乱(比如代码块没高亮),说明你漏了 CSS:goldmark 不生成 class,要自己加 <pre class="brush:php;toolbar:false;">&lt;code class=&quot;go&quot;&gt;&lt;/code&gt;,然后靠 &lt;code&gt;highlight.js&lt;/code&gt; 渲染</pre>

    容易被忽略的一点:Go 返回的 JSON 必须是 {"html": "..."} 结构,前端别去拆 response.text() 再手动 parse —— 直接 response.json()html 字段。

text=ZqhQzanResources