大致看上去,和我们不使用renderer包的实现基本一样。指定Content-Type, 发送HTTP状态码,然后看JSON前缀是否设置,如果设置,前缀也发送到字节流中。 最后就是发送json字节流。
唯一区别在于,我们使用encoding/json包的Marshal方法来将给定的值转换成二进制序列,而renderer对这个方法进行了包装:
func (r *Render) json(v interface{}) ([]byte, error) {
var bs []byte
var err error
if r.opts.JSONIndent {
bs, err = json.MarshalIndent(v, "", " ")
} else {
bs, err = json.Marshal(v)
}
if err != nil {
return bs, err
}
if r.opts.UnEscapeHTML {
bs = bytes.Replace(bs, []byte("u003c"), []byte("<"), -1)
bs = bytes.Replace(bs, []byte("u003e"), []byte(">"), -1)
bs = bytes.Replace(bs, []byte("u0026"), []byte("&"), -1)
}
return bs, nil
}
如果有设置JSONIndent, 即JSON缩进,那么使用json.MarshalIndent来将变量转换为json字节流。 这个方法其实就是将JSON格式化,使得结果看起来更好看。
另外这个方法还会根据配置,进行html实体的转义。
因此总体来说原理和开头的代码基本一样。只不过多了一些额外的修饰修补。
JSONP方法
我们理解了JSON方法,理解起JSONP就更加简单了。
JSONP全称为JSON with Padding, 用于解决Ajax跨域问题的一种方案。
它的原理非常简单:
// 客户端代码
var dosomething = function(data) {
// do something with data
}
var url = "server.jsonp?callback=dosomething";
// 创建 <script> 标签,设置其 src 属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把 <script> 标签加入 <body> 尾部,此时调用开始。
document.getElementsByTagName('body')[0].appendChild(script);
上面server.jsonp是一个后台脚本,访问后立即返回它的输出内容, 这也就是renderer的JSONP要响应的内容。
func (r *Render) JSONP(w http.ResponseWriter, status int, callback string, v interface{}) error {
w.Header().Set(ContentType, r.opts.ContentJSONP)
w.WriteHeader(status)
bs, err := r.json(v)
if err != nil {
return err
}
if callback == "" {
return errors.New("renderer: callback can not bet empty")
}
w.Write([]byte(callback + "("))
_, err = w.Write(bs)
w.Write([]byte(");"))
return err
}
-
设置Content-Type为application/javascript, 非常关键的一点。 想一想html中嵌入的js文件的mime类型是不是也是这个值?
然后同样的设置响应状态码, 这点没有什么特殊的。
将值转换为json字节序列。这个json字节序列还没有向响应写入进去。
这个时候我们检查callback是否存在,不存在报错出去。因为是JSONP, 必须要有callback, 这个callback是请求参数传入的。
然后用"callbak("和")"将json字节序列包围起来,一起输出到响应流中。这样jsonp响应就产生了。










