GO:借助html/template实现include子模板

不引入liquid支持库,借助原有的html/template来支持include子模板功能。

思路:

  1. 模仿Django,默认从templates文件夹下读取模板,已经取到的模板加入到map结构的简单cache中缓存
  2. 利用正则表达式在模板中发现 {% include xxx %}时,将其作为一个文件递归加载到当前模板中来,如果加载失败(如文件不存在等), 那么将其保持原样
  3. 利用html/template完成变量等的载入

规定格式时不能使用{{}}这种,因为template会认为这是一条它支持的指令,一旦在第2步中加载失败,就会保持原样,继续传递到template parser中去。

另外需要设置一个观察值,在进行递归include时防止出现递归包含自己的死循环。

代码实现:

func RenderWithTemplate(writer io.Writer, templateName string, data map[string]interface{}) error {
    rawTemplate, err := LoadTemplate(templateName)
    if err != nil {
        return err
    }
    tmpl, err := template.New("new").Parse(rawTemplate)
    if err != nil {
        return err
    }
    return tmpl.Execute(writer, data)
}

var TemplateCache map[string]string = nil
var gRecursiveLoadTemplateCount = 0
// Load template with file name templateName under 'templates' dir
// If it contains {% include xxx %}, it will load content of that
// template into current template.
// **Note**: template name inside include directive should not be wrapped with ''
func LoadTemplate(templateName string) (string, error) {
    if result, ok := TemplateCache[templateName]; ok {
        return result, nil
    }

    templatePath := "templates/" + templateName
    file, err := os.Open(templatePath)
    if err != nil {
        return "", err
    }

    stat, err := file.Stat()
    if err != nil {
        return "", err
    }

    buffer := make([]byte, stat.Size())
    _, err = file.Read(buffer)
    if TemplateCache == nil {
        TemplateCache = make(map[string]string)
    }
    rawTemplate := string(buffer)
    re := regexp.MustCompile("{%\\s*include\\s+(.*?)\\s*%}")
    matches := re.FindAllStringSubmatchIndex(rawTemplate, -1)
    if len(matches) > 0 {
        newTemplate := ""
        start := 0
        for _, m := range matches {
            if len(m) == 4 {
                newTemplate += rawTemplate[start:m[0]]
                includeName := rawTemplate[m[2]:m[3]]

                gRecursiveLoadTemplateCount ++
                if gRecursiveLoadTemplateCount > 10 {
                    log.Fatal("Possible dead include loop in file: ", templateName)
                }
                includedTemplate, err := LoadTemplate(includeName)
                gRecursiveLoadTemplateCount --

                if err != nil {
                    fmt.Println("Failed to include file ", includeName, " from ", templateName, "err:", err)
                    includedTemplate = rawTemplate[m[0]:m[1]]  // Keep it as original
                }
                start = m[1]
                newTemplate += includedTemplate
            }
        }
        if start < len(rawTemplate) {
            newTemplate += rawTemplate[start:]
        }
        rawTemplate = newTemplate
    }
    TemplateCache[templateName] = rawTemplate
    return rawTemplate, nil
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,404评论 19 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,932评论 18 399
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 175,447评论 25 709
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,054评论 6 342
  • 一个人的眼泪
    SophiaWHAN阅读 952评论 0 0