浅谈go语言renderer包代码分析

2019-11-10 11:50:42刘景俊

用于创建Render类型的函数。它接受Options类型的参数,返回一个Render类型。

我们一般通常不传入Options类型变量调用renderer.New()来创建一个Render类型。

  var opt Options
  if opts != nil {
    opt = opts[0]
  }

  r := &Render{
    opts:   opt,
    templates: make(map[string]*template.Template),
  }

上面这段代码实际上就是初始化了一个Render类型的r变量。opts为nil, templates为map类型,这里被初始化。

接下来调用r.buildOptions()方法。

buildOptions方法

func (r *Render) buildOptions() {
  if r.opts.Charset == "" { // 没有指定编码方式,使用默认的编码方式UTF-8
    r.opts.Charset = defaultCharSet
  }

  if r.opts.JSONPrefix == "" { // 没有指定JSON前缀,使用默认的
    r.opts.JSONPrefix = defaultJSONPrefix
  }

  if r.opts.XMLPrefix == "" { // 没有指定XML前缀,使用默认XML前缀
    r.opts.XMLPrefix = defaultXMLPrefix
  }

  if r.opts.TemplateExtension == "" { // 模版扩展名设置
    r.opts.TemplateExtension = "." + defaultTemplateExt
  } else {
    r.opts.TemplateExtension = "." + r.opts.TemplateExtension
  }

  if r.opts.LayoutExtension == "" { // 布局扩展名设置
    r.opts.LayoutExtension = "." + defaultLayoutExt
  } else {
    r.opts.LayoutExtension = "." + r.opts.LayoutExtension
  }

  if r.opts.LeftDelim == "" { // 模版变量左分割符设置
    r.opts.LeftDelim = defaultTemplateLeftDelim
  }

  if r.opts.RightDelim == "" { // 模版变量右分割符设置
    r.opts.RightDelim = defaultTemplateRightDelim
  }

  // 设置内容类型属性常量
  r.opts.ContentJSON = ContentJSON 
  r.opts.ContentJSONP = ContentJSONP
  r.opts.ContentXML = ContentXML
  r.opts.ContentYAML = ContentYAML
  r.opts.ContentHTML = ContentHTML
  r.opts.ContentText = ContentText
  r.opts.ContentBinary = ContentBinary

  // 如果没有禁用编码集,那么就将内容类型后面添加字符集属性。
  if !r.opts.DisableCharset {
    r.enableCharset()
  }
}

该方法构建Render的opts属性,并绑定默认的值。

我们看了New函数,得到了一个Render类型,接下来就是呈现具体类型的内容。我们以JSON为例,看看它怎么实现的。

JSON方法

如果没有renderer包,我们想要用Go语言发送JSON数据响应,我们的实现代码大致如下:

func DoSomething(w http.ResponseWriter, ...) {
  // json from a variable v
  jData, err := json.Marshal(v)
  if err != nil {
    panic(err)
    return
  }
  w.Header().Set("Content-Type", "application/json")
  w.WriteHeader(200)
  w.Write(jData)
}

原理很简单,首先从变量中解析出JSON, 然后发送Content-Type为application/json, 然后发送状态码, 最后将json序列发送出去。

那么我们再详细看看renderer中的JSON方法的实现:

func (r *Render) JSON(w http.ResponseWriter, status int, v interface{}) error {
  w.Header().Set(ContentType, r.opts.ContentJSON)
  w.WriteHeader(status)

  bs, err := r.json(v)
  if err != nil {
    return err
  }
  if r.opts.JSONPrefix != "" {
    w.Write([]byte(r.opts.JSONPrefix))
  }
  _, err = w.Write(bs)
  return err
}