亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Golang > 正文

淺談go語言renderer包代碼分析

2020-04-01 18:57:29
字體:
來源:轉載
供稿:網友

renderer是Go語言的一個簡單的、輕量的、快速響應的呈現包,它可以支持JSON、JSONP、XML、HYAML、HTML、File等類型的響應。在開發web應用或RESTFul API的時候,這個包是非常方便的toolkit。

本文繞開如何使用它,深入到代碼實現中研究它,同時也嘗嘗Go語言包的開發套路。

Go包基礎介紹

代碼結構

package pkgnameimport (  "fmt"  ...)const (  CONST1 typeX = xx  ...)var (  VAR1 typeX = xxx  ...)func Fn1() {}

在Go語言中包名和目錄名保持一致,同一包內可共享命名空間。

  1. 包文件開頭除了注釋外,第一行,必須是package pkgname, 聲明包的名稱。
  2. 在包聲明之后,可以import標準庫中的包和其他外部包。
  3. 然后可以定義包常量、包變量(暴露變量和非暴露變量,以首字母大小寫來區分實現)。
  4. 然后定義自定義類型、函數或方法。

import語句

import可以引入標準庫的包,也可以引入外部包。Go語言中一旦引入某個包,必須在程序中使用到這個包的命名空間,否則編譯報錯會告訴你引入了某個包,但代碼中未曾使用。

當然你也會有疑問,我如果需要引入包,但又不想使用怎么辦。這個Go語言有一個特殊的符號"_", 放在引入包名前面,就可以防止編譯報錯。為什么會有這種考慮呢? 因為有時候,我們只是希望引入一個包,然后執行這個包的一些初始化設置。然后在代碼中暫時不使用該包的任何方法和變量。

import (  _ "gitHub.com/xxxxx/pkgname")

上面語句會引入pkgname命名空間,但是暫時不在代碼中使用這個命名空間。這樣引入之后,會在pkgname包中尋找init()函數,然后在main()函數執行之前先執行它們,這點對于需要使用包之前做初始化非常有用。

暴露與非暴露的實現

我們在其他編程語言中,都接觸過private, protected, public之類的修飾符。 但是在Go語言中完全沒有這些,但是Go語言還是可以某些東西從包中暴露出去,而某些東西不暴露出去,它用的原則很簡單的,就是標識符如果以小寫字母開頭的,包外不可見; 而如果是標識符以大寫字符開頭的,包外可見,可訪問。

對于暴露變量和函數(方法)非常直觀簡單,但是如果是暴露的結構體,情況稍微復雜一點。 不過本質上也是差不多, 結構體外部如果小寫字母開頭,內部屬性大寫字母開頭。 則外部包直接不訪問,但如果通過函數或方法返回這個外部類型,那么可以通過:=得到這個外部類型,從而可以訪問其內部屬性。舉例如下:

// package pkgnamepackage pkgnametype admin struct {  Name string  Email String}func Admin() *admin {  return &admin{    Name: "admin",    Email: "admin@email.com",  }}

那么我們在外部包中,可以直接通過下面代碼訪問admin結構體內部的屬性:

admin := pkgname.Admin()fmt.Println(admin.Name, admin.Email)

當然這種情況下,需要你事先知道admin的結構以及包含的屬性名。

內置類型和自定義類型

Go語言包含了幾種簡單的內置類型:整數、布爾值、數組、字符串、分片、映射等。除了內置類型,Go語言還支持方便的自定義類型。

自定義類型有兩種:

  1. 自定義結構體類型: type MyType struct {}這種形式定義,這種類似C語言中的結構體定義。
  2. 命名類型: type MyInt int。這種方式通過將內置類型或自定義類型命名為新的類型的方式來實現。 需要注意MyInt和int是不同的類型,它們之間不能直接互相賦值。

函數和方法

Go語言的函數和方法都是使用func關鍵詞聲明的,方法和函數的唯一區別在于,方法需要綁定目標類型; 而函數則無需綁定。

type MyType struct {}// 這是函數func DoSomething() {}// 這是方法func (mt MyType) MyMethod() {}// 或者另外一種類型的方法func (mt *MyType) MyMethod2() {}

對于方法來說,需要綁定一個receiver, 我稱之為接收者。 接收者有兩種類型:

  1. 值類型的接收者
  2. 指針類型的接收者

關于接收者和接口部分,有很多需要延伸的,后續有時間整理補充出來。

接口

代碼分析

常量部分

代碼分析部分,我們先跳過import部分, 直接進入到常量的聲明部分。

const (  // ContentType represents content type  ContentType string = "Content-Type"  // ContentJSON represents content type application/json  ContentJSON string = "application/json"  // ContentJSONP represents content type application/javascript  ContentJSONP string = "application/javascript"  // ContentXML represents content type application/xml  ContentXML string = "application/xml"  // ContentYAML represents content type application/x-yaml  ContentYAML string = "application/x-yaml"  // ContentHTML represents content type text/html  ContentHTML string = "text/html"  // ContentText represents content type text/plain  ContentText string = "text/plain"  // ContentBinary represents content type application/octet-stream  ContentBinary string = "application/octet-stream"  // ContentDisposition describes contentDisposition  ContentDisposition string = "Content-Disposition"  // contentDispositionInline describes content disposition type  contentDispositionInline string = "inline"  // contentDispositionAttachment describes content disposition type  contentDispositionAttachment string = "attachment"  defaultCharSet      string = "utf-8"  defaultJSONPrefix     string = ""  defaultXMLPrefix     string = `<?xml version="1.0" encoding="ISO-8859-1" ?>/n`  defaultTemplateExt    string = "tpl"  defaultLayoutExt     string = "lout"  defaultTemplateLeftDelim string = "{{"  defaultTemplateRightDelim string = "}}")

以上常量聲明了內容類型常量以及本包支持的各種內容類型MIME值。以及各種具體內容類型相關的常量,比如字符編碼方式、JSONP前綴、XML前綴,模版左右分割符等等一些常量。

類型聲明部分

這部分聲明了如下類型:

  1. M: 映射類型,描述代表用于發送的響應數據便捷類型。
  2. Options: 描述選項類型。
  3. Render: 用于描述renderer類型。
type (  // M describes handy type that represents data to send as response  M map[string]interface{}  // Options describes an option type  Options struct {    // Charset represents the Response charset; default: utf-8    Charset string    // ContentJSON represents the Content-Type for JSON    ContentJSON string    // ContentJSONP represents the Content-Type for JSONP    ContentJSONP string    // ContentXML represents the Content-Type for XML    ContentXML string    // ContentYAML represents the Content-Type for YAML    ContentYAML string    // ContentHTML represents the Content-Type for HTML    ContentHTML string    // ContentText represents the Content-Type for Text    ContentText string    // ContentBinary represents the Content-Type for octet-stream    ContentBinary string    // UnEscapeHTML set UnEscapeHTML for JSON; default false    UnEscapeHTML bool    // DisableCharset set DisableCharset in Response Content-Type    DisableCharset bool    // Debug set the debug mode. if debug is true then every time "VIEW" call parse the templates    Debug bool    // JSONIndent set JSON Indent in response; default false    JSONIndent bool    // XMLIndent set XML Indent in response; default false    XMLIndent bool    // JSONPrefix set Prefix in JSON response    JSONPrefix string    // XMLPrefix set Prefix in XML response    XMLPrefix string    // TemplateDir set the Template directory    TemplateDir string    // TemplateExtension set the Template extension    TemplateExtension string    // LeftDelim set template left delimiter default is {{    LeftDelim string    // RightDelim set template right delimiter default is }}    RightDelim string    // LayoutExtension set the Layout extension    LayoutExtension string    // FuncMap contain function map for template    FuncMap []template.FuncMap    // ParseGlobPattern contain parse glob pattern    ParseGlobPattern string  }  // Render describes a renderer type  Render struct {    opts     Options    templates   map[string]*template.Template    globTemplates *template.Template    headers    map[string]string  })

New函數

// New return a new instance of a pointer to Renderfunc New(opts ...Options) *Render {  var opt Options  if opts != nil {    opt = opts[0]  }  r := &Render{    opts:   opt,    templates: make(map[string]*template.Template),  }  // build options for the Render instance  r.buildOptions()  // if TemplateDir is not empty then call the parseTemplates  if r.opts.TemplateDir != "" {    r.parseTemplates()  }  // ParseGlobPattern is not empty then parse template with pattern  if r.opts.ParseGlobPattern != "" {    r.parseGlob()  }  return r}

用于創建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}

大致看上去,和我們不使用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}
  1. 設置Content-Type為application/javascript, 非常關鍵的一點。 想一想html中嵌入的js文件的mime類型是不是也是這個值?
  2. 然后同樣的設置響應狀態碼, 這點沒有什么特殊的。
  3. 將值轉換為json字節序列。這個json字節序列還沒有向響應寫入進去。
  4. 這個時候我們檢查callback是否存在,不存在報錯出去。因為是JSONP, 必須要有callback, 這個callback是請求參數傳入的。
  5. 然后用"callbak("和")"將json字節序列包圍起來,一起輸出到響應流中。這樣jsonp響應就產生了。

那么回過頭結合我們開頭寫的一個前段jsonp代碼,我們知道請求了server.jsonp?callback=xxxx之后,一個application/javascript的內容被嵌入到body內。它是js文件。 而其內容將callback替換為傳入的dosomething, 我們得到類似的js內容:

dosomething({  // ....});

這樣服務端產生數據,并調用前端js的方法, 傳入這些數據, jsonp就完成了。這樣的js一旦加載成功,它和當前訪問域名是同源的,不存在跨域問題。 這樣就解決了ajax跨域問題。

剩下的其他方法基本都是同樣的套路, 這里不再贅述, 有時間的話再重新整理下開頭的內容。

本文僅個人學習整理, 如有不對之處, 還望各位不吝指出。

在鏈接部分,有我自己對Go in Action英文書籍的翻譯, 英文比較差,再者也是初學Go語言,翻譯不到位, 有興趣的朋友可以一起翻譯此書,或者后續有其他好的技術書籍,一起翻譯學習。

引用鏈接

  1. renderer
  2. Go In Action

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久国产精品99国产精| 亚洲成色777777女色窝| 亚洲爱爱爱爱爱| 自拍偷拍亚洲在线| 秋霞av国产精品一区| 国产免费观看久久黄| 亚洲欧美日韩第一区| 色中色综合影院手机版在线观看| 久久久999精品| 国产成人精品久久亚洲高清不卡| 欧美日本黄视频| 亚洲精选一区二区| 精品国产自在精品国产浪潮| 国产极品jizzhd欧美| 亚洲美女在线看| 国产精品九九久久久久久久| 国产午夜精品免费一区二区三区| 国产精品入口尤物| 国产在线播放91| 久久成人人人人精品欧| 日韩欧美成人精品| 成人欧美一区二区三区黑人孕妇| 久久久免费观看| 欧美日韩国产在线看| 亚洲xxxx做受欧美| 午夜精品久久久99热福利| 51久久精品夜色国产麻豆| 91系列在线播放| 欧美肥老妇视频| 亚洲欧美制服丝袜| 欧美精品久久久久| 欧美激情区在线播放| 88xx成人精品| 欧美激情精品久久久久| 欧美激情中文字幕在线| 亚洲国产欧美一区二区三区同亚洲| 国产成人亚洲综合91精品| 日韩精品在线观| 91精品国产免费久久久久久| 欧美黄色免费网站| 欧美激情亚洲国产| 日韩欧美精品在线观看| 欧美日韩国产专区| 一区二区国产精品视频| 1769国内精品视频在线播放| 久久久久久综合网天天| 精品福利樱桃av导航| 日本亚洲精品在线观看| 亚洲欧美精品suv| 精品久久香蕉国产线看观看亚洲| 色悠久久久久综合先锋影音下载| 欧美日韩国产二区| 日本一区二区在线播放| 国产精品第1页| 欧美另类极品videosbestfree| 国产成人高清激情视频在线观看| 日韩中文字幕国产| 亚洲成人黄色在线观看| 精品成人av一区| 久久亚洲一区二区三区四区五区高| 国产精品都在这里| 国产精品27p| 亚洲加勒比久久88色综合| 国产成人免费av电影| 国产精品久久久久7777婷婷| 色偷偷av亚洲男人的天堂| 深夜福利日韩在线看| 国内免费精品永久在线视频| 在线中文字幕日韩| 国产成人精品综合| 神马久久桃色视频| 亚洲片在线资源| 91国偷自产一区二区三区的观看方式| 欧美专区中文字幕| 欧美日韩国产中文字幕| 亚洲欧美国产视频| 久久99精品久久久久久青青91| 伊人一区二区三区久久精品| 久久久久亚洲精品国产| 国产精品精品国产| 国产精品视频免费在线观看| 国产精品96久久久久久又黄又硬| 欧美与欧洲交xxxx免费观看| 91高清在线免费观看| 欧美午夜激情在线| 国产精品福利在线观看| 亚洲精品免费一区二区三区| 欧美精品激情视频| 成人午夜高潮视频| 精品久久久久久中文字幕大豆网| 国产精品视频地址| 日韩高清电影免费观看完整| 日韩精品高清在线观看| 亚洲精品乱码久久久久久按摩观| 日本欧美精品在线| 91精品国产乱码久久久久久蜜臀| 亚洲精品中文字幕有码专区| 亚洲视频在线免费看| 欧美又大又粗又长| 国产视频精品va久久久久久| 成人淫片在线看| 亚洲一区二区中文| www国产精品视频| 欧美精品xxx| 日韩免费在线播放| 亚洲男人天堂2023| 98视频在线噜噜噜国产| 国产精品三级美女白浆呻吟| 欧美怡春院一区二区三区| 亚洲精品国产电影| 91亚洲精品久久久久久久久久久久| 97人人爽人人喊人人模波多| 欧美小视频在线观看| 中文字幕国内精品| 全球成人中文在线| 一本色道久久88综合亚洲精品ⅰ| 国产激情999| 欧美性做爰毛片| 一本色道久久88综合亚洲精品ⅰ| 国内精品久久久久久中文字幕| 懂色av影视一区二区三区| 国产精品第1页| 久久国产精彩视频| 欧美华人在线视频| 久久久久久久久久久91| 日韩精品在线播放| 国产91精品视频在线观看| 欧美做受高潮电影o| 日韩电影免费观看在线| xxx成人少妇69| 亚洲天堂av在线播放| 亚洲欧美福利视频| 日韩黄色av网站| 57pao成人国产永久免费| 欧美激情中文字幕在线| 国产美女主播一区| 国产成人精品电影| 亚洲女同性videos| 亚洲第一精品电影| 韩国国内大量揄拍精品视频| 日韩精品中文字幕在线播放| 亚洲精品第一页| 久久亚洲国产成人| 91精品国产电影| 日韩成人久久久| 亚洲3p在线观看| 久久亚洲影音av资源网| 日韩免费观看在线观看| 久久精品国产69国产精品亚洲| 18一19gay欧美视频网站| 亚洲图片欧美午夜| 精品国产依人香蕉在线精品| 在线视频欧美日韩| 久久99精品久久久久久琪琪| 日韩视频在线观看免费| 一区二区三区视频免费在线观看| 欧美成人精品在线| 91精品视频大全| 亚洲加勒比久久88色综合| 精品久久久久久久久久| 91av在线影院| 色综合91久久精品中文字幕| 国产精品久久久久久搜索| 久久精品国产欧美亚洲人人爽|