去提问»
提问背景 Question:
我添加了解析md格式的函数,但好像没有效果,你看看下面的代码。// 2. 百科详情页 Handler // 路由配置:r.GET("/baike/article/:id", ai.Baike_detail) func Baike_detail(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { articleIDStr := ps.ByName("id") articleID, err := strconv.Atoi(articleIDStr) if err != nil { http.NotFound(w, r) return } // 1. 查询文章主体 var a Article var contentRaw string var createdAtInt int64 row := fun.DB.QueryRow( "SELECT id, question, title, description, content, click_count, like_count, created_at FROM cms_article WHERE id = ? AND status = 1", articleID, ) err = row.Scan(&a.ID, &a.Question, &a.Title, &a.Description, &contentRaw, &a.ClickCount, &a.LikeCount, &createdAtInt) if err != nil { if err == sql.ErrNoRows { http.NotFound(w, r) } else { http.Error(w, "数据库错误: "+err.Error(), http.StatusInternalServerError) } return } //a.Content = template.HTML(contentRaw) // 转换为非转义 HTML,直接渲染 a.Content = ConvertMarkdown(contentRaw) //解析Markdown并转义template.HTML a.CreatedAt = time.Unix(createdAtInt, 0).Format("2006-01-02 15:04") // 2. 异步增加浏览量(避免阻塞主渲染) go func(id int) { _, _ = fun.DB.Exec("UPDATE cms_article SET click_count = click_count + 1 WHERE id = ?", id) }(articleID) // 3. 查询当前文章的 Tags var tags []Tag tagRows, err := fun.DB.Query(` SELECT t.id, t.name FROM cms_tag t INNER JOIN cms_article_tag at ON t.id = at.tag_id WHERE at.article_id = ?`, articleID, ) if err == nil { for tagRows.Next() { var t Tag if err := tagRows.Scan(&t.ID, &t.Name); err == nil { tags = append(tags, t) } } tagRows.Close() } data := BaikeDetailData{ Article: a, Tags: tags, } // 4. 渲染模板 path := filepath.Join(fun.AppPath, "/templates/ai/baike_detail.html") mb, err := fun.TManager.Get(path) if err != nil { http.Error(w, "详情模板渲染失败: "+err.Error(), http.StatusInternalServerError) return } err = mb.Execute(w, data) if err != nil { fun.WriteLog("渲染百科详情页模板报错: %s", err) } } func ConvertMarkdown(mdContent string) template.HTML { var buf bytes.Buffer if err := goldmark.Convert([]byte(mdContent), &buf); err != nil { return template.HTML(mdContent) // 如果解析失败,回退返回原始文本 } return template.HTML(buf.String()) }

解决 Go 百科 Markdown 解析不生效问题

发布时间:2026-08-18 11:28 👁️ 阅读:1

你的 Markdown 解析函数 ConvertMarkdown 在逻辑上是完全正确的。如果在页面上**“没有效果”**(表现为直接显示了原始的 Markdown 语法,或者页面上直接把 HTML 标签 <p><h1> 当作纯文本显示了出来),通常是由以下两个原因导致的:

核心原因排查

原因 1:Article 结构体的 Content 字段类型定义为了 string

如果你的 Article 结构体定义中,Content 字段是 string 类型:

type Article struct {
    ID      int
    Title   string
    Content string // 如果这里是 string!
    // ...
}

那么即使你在 Go 代码里写了 a.Content = string(ConvertMarkdown(contentRaw)),在 Go 的 html/template 模板引擎渲染时,它依然会把 a.Content 当作普通字符串,自动将其中的 HTML 标签全部转义(例如把 <p> 转义为 &lt;p&gt;),导致浏览器无法解析成富文本。

【解决方案】
必须将 Article 结构体中的 Content 字段类型定义为 template.HTML

import "html/template"

type Article struct {
    ID          int
    Question    string
    Title       string
    Description string
    Content     template.HTML // 必须是 template.HTML 类型
    ClickCount  int
    LikeCount   int
    CreatedAt   string
}

原因 2:Goldmark 默认过滤了 HTML 标签(Unsafe 策略)

如果你的 Markdown 文本中本身夹杂了 HTML 标签,goldmark 默认出于安全考虑,会过滤或者转义这些 HTML。如果需要保留原生的 HTML 渲染,需要开启 WithUnsafe() 选项。

【解决方案】
修改你的 ConvertMarkdown 函数,引入 goldmarkrenderer 选项:

import (
	"bytes"
	"html/template"
	"github.com/yuin/goldmark"
	"github.com/yuin/goldmark/renderer"
)

func ConvertMarkdown(mdContent string) template.HTML {
	var buf bytes.Buffer
	// 创建一个支持渲染原生 HTML 的 goldmark 实例
	md := goldmark.New(
		goldmark.WithRendererOptions(
			renderer.WithUnsafe(), // 允许渲染未安全过滤的 HTML 标签
		),
	)
	
	if err := md.Convert([]byte(mdContent), &buf); err != nil {
		return template.HTML(mdContent)
	}
	return template.HTML(buf.String())
}

原因 3:HTML 模板中的标签调用方式不正确

在你的 baike_detail.html 模板中,输出内容的地方需要确保直接输出了该字段。因为在 Go 后端已经把 Article.Content 包装为了 template.HTML 安全类型,模板里直接调用即可,不需要添加额外的转义处理:

<!-- 在你的 HTML 模板中 -->
<div class="prose max-w-none">
    {{ .Article.Content }}
</div>

总结排查清单

  1. 检查 Article 结构体的 Content 字段类型是否为 template.HTML
  2. 检查 import 中是否引入了 html/template 而不是 text/template
  3. 如果 Markdown 中有 HTML 标签,确保使用了 renderer.WithUnsafe() 配置。