校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁(yè) > 知識(shí)庫(kù) > 淺談go語(yǔ)言renderer包代碼分析

淺談go語(yǔ)言renderer包代碼分析

熱門標(biāo)簽:黃岡人工智能電銷機(jī)器人哪個(gè)好 惠州電銷防封電話卡 建造者2地圖標(biāo)注 釘釘有地圖標(biāo)注功能嗎 阿里云ai電話機(jī)器人 鄭州亮點(diǎn)科技用的什么外呼系統(tǒng) 汕頭小型外呼系統(tǒng) 浙江高頻外呼系統(tǒng)多少錢一個(gè)月 濱州自動(dòng)電銷機(jī)器人排名

renderer是Go語(yǔ)言的一個(gè)簡(jiǎn)單的、輕量的、快速響應(yīng)的呈現(xiàn)包,它可以支持JSON、JSONP、XML、HYAML、HTML、File等類型的響應(yīng)。在開發(fā)web應(yīng)用或RESTFul API的時(shí)候,這個(gè)包是非常方便的toolkit。

本文繞開如何使用它,深入到代碼實(shí)現(xiàn)中研究它,同時(shí)也嘗嘗Go語(yǔ)言包的開發(fā)套路。

Go包基礎(chǔ)介紹

代碼結(jié)構(gòu)

package pkgname

import (
  "fmt"
  ...
)
const (
  CONST1 typeX = xx
  ...
)

var (
  VAR1 typeX = xxx
  ...
)

func Fn1() {
}

在Go語(yǔ)言中包名和目錄名保持一致,同一包內(nèi)可共享命名空間。

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

import語(yǔ)句

import可以引入標(biāo)準(zhǔn)庫(kù)的包,也可以引入外部包。Go語(yǔ)言中一旦引入某個(gè)包,必須在程序中使用到這個(gè)包的命名空間,否則編譯報(bào)錯(cuò)會(huì)告訴你引入了某個(gè)包,但代碼中未曾使用。

當(dāng)然你也會(huì)有疑問(wèn),我如果需要引入包,但又不想使用怎么辦。這個(gè)Go語(yǔ)言有一個(gè)特殊的符號(hào)"_", 放在引入包名前面,就可以防止編譯報(bào)錯(cuò)。為什么會(huì)有這種考慮呢? 因?yàn)橛袝r(shí)候,我們只是希望引入一個(gè)包,然后執(zhí)行這個(gè)包的一些初始化設(shè)置。然后在代碼中暫時(shí)不使用該包的任何方法和變量。

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

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

暴露與非暴露的實(shí)現(xiàn)

我們?cè)谄渌幊陶Z(yǔ)言中,都接觸過(guò)private, protected, public之類的修飾符。 但是在Go語(yǔ)言中完全沒(méi)有這些,但是Go語(yǔ)言還是可以某些東西從包中暴露出去,而某些東西不暴露出去,它用的原則很簡(jiǎn)單的,就是標(biāo)識(shí)符如果以小寫字母開頭的,包外不可見(jiàn); 而如果是標(biāo)識(shí)符以大寫字符開頭的,包外可見(jiàn),可訪問(wèn)。

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

// package pkgname
package pkgname

type admin struct {
  Name string
  Email String
}

func Admin() *admin {
  return admin{
    Name: "admin",
    Email: "admin@email.com",
  }
}

那么我們?cè)谕獠堪校梢灾苯油ㄟ^(guò)下面代碼訪問(wèn)admin結(jié)構(gòu)體內(nèi)部的屬性:

admin := pkgname.Admin()

fmt.Println(admin.Name, admin.Email)

當(dāng)然這種情況下,需要你事先知道admin的結(jié)構(gòu)以及包含的屬性名。

內(nèi)置類型和自定義類型

Go語(yǔ)言包含了幾種簡(jiǎn)單的內(nèi)置類型:整數(shù)、布爾值、數(shù)組、字符串、分片、映射等。除了內(nèi)置類型,Go語(yǔ)言還支持方便的自定義類型。

自定義類型有兩種:

  1. 自定義結(jié)構(gòu)體類型: type MyType struct {}這種形式定義,這種類似C語(yǔ)言中的結(jié)構(gòu)體定義。
  2. 命名類型: type MyInt int。這種方式通過(guò)將內(nèi)置類型或自定義類型命名為新的類型的方式來(lái)實(shí)現(xiàn)。 需要注意MyInt和int是不同的類型,它們之間不能直接互相賦值。

函數(shù)和方法

Go語(yǔ)言的函數(shù)和方法都是使用func關(guān)鍵詞聲明的,方法和函數(shù)的唯一區(qū)別在于,方法需要綁定目標(biāo)類型; 而函數(shù)則無(wú)需綁定。

type MyType struct {
}

// 這是函數(shù)
func DoSomething() {

}

// 這是方法
func (mt MyType) MyMethod() {
}

// 或者另外一種類型的方法
func (mt *MyType) MyMethod2() {
}

對(duì)于方法來(lái)說(shuō),需要綁定一個(gè)receiver, 我稱之為接收者。 接收者有兩種類型:

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

關(guān)于接收者和接口部分,有很多需要延伸的,后續(xù)有時(shí)間整理補(bǔ)充出來(lái)。

接口

代碼分析

常量部分

代碼分析部分,我們先跳過(guò)import部分, 直接進(jìn)入到常量的聲明部分。

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 = "}}"
)

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

類型聲明部分

這部分聲明了如下類型:

  1. M: 映射類型,描述代表用于發(fā)送的響應(yīng)數(shù)據(jù)便捷類型。
  2. Options: 描述選項(xiàng)類型。
  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函數(shù)

// New return a new instance of a pointer to Render
func 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
}

用于創(chuàng)建Render類型的函數(shù)。它接受Options類型的參數(shù),返回一個(gè)Render類型。

我們一般通常不傳入Options類型變量調(diào)用renderer.New()來(lái)創(chuàng)建一個(gè)Render類型。

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

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

上面這段代碼實(shí)際上就是初始化了一個(gè)Render類型的r變量。opts為nil, templates為map類型,這里被初始化。

接下來(lái)調(diào)用r.buildOptions()方法。

buildOptions方法

func (r *Render) buildOptions() {
  if r.opts.Charset == "" { // 沒(méi)有指定編碼方式,使用默認(rèn)的編碼方式UTF-8
    r.opts.Charset = defaultCharSet
  }

  if r.opts.JSONPrefix == "" { // 沒(méi)有指定JSON前綴,使用默認(rèn)的
    r.opts.JSONPrefix = defaultJSONPrefix
  }

  if r.opts.XMLPrefix == "" { // 沒(méi)有指定XML前綴,使用默認(rèn)XML前綴
    r.opts.XMLPrefix = defaultXMLPrefix
  }

  if r.opts.TemplateExtension == "" { // 模版擴(kuò)展名設(shè)置
    r.opts.TemplateExtension = "." + defaultTemplateExt
  } else {
    r.opts.TemplateExtension = "." + r.opts.TemplateExtension
  }

  if r.opts.LayoutExtension == "" { // 布局?jǐn)U展名設(shè)置
    r.opts.LayoutExtension = "." + defaultLayoutExt
  } else {
    r.opts.LayoutExtension = "." + r.opts.LayoutExtension
  }

  if r.opts.LeftDelim == "" { // 模版變量左分割符設(shè)置
    r.opts.LeftDelim = defaultTemplateLeftDelim
  }

  if r.opts.RightDelim == "" { // 模版變量右分割符設(shè)置
    r.opts.RightDelim = defaultTemplateRightDelim
  }

  // 設(shè)置內(nèi)容類型屬性常量
  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

  // 如果沒(méi)有禁用編碼集,那么就將內(nèi)容類型后面添加字符集屬性。
  if !r.opts.DisableCharset {
    r.enableCharset()
  }
}

該方法構(gòu)建Render的opts屬性,并綁定默認(rèn)的值。

我們看了New函數(shù),得到了一個(gè)Render類型,接下來(lái)就是呈現(xiàn)具體類型的內(nèi)容。我們以JSON為例,看看它怎么實(shí)現(xiàn)的。

JSON方法

如果沒(méi)有renderer包,我們想要用Go語(yǔ)言發(fā)送JSON數(shù)據(jù)響應(yīng),我們的實(shí)現(xiàn)代碼大致如下:

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)
}

原理很簡(jiǎn)單,首先從變量中解析出JSON, 然后發(fā)送Content-Type為application/json, 然后發(fā)送狀態(tài)碼, 最后將json序列發(fā)送出去。

那么我們?cè)僭敿?xì)看看renderer中的JSON方法的實(shí)現(xiàn):

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包的實(shí)現(xiàn)基本一樣。指定Content-Type, 發(fā)送HTTP狀態(tài)碼,然后看JSON前綴是否設(shè)置,如果設(shè)置,前綴也發(fā)送到字節(jié)流中。 最后就是發(fā)送json字節(jié)流。

唯一區(qū)別在于,我們使用encoding/json包的Marshal方法來(lái)將給定的值轉(zhuǎn)換成二進(jìn)制序列,而renderer對(duì)這個(gè)方法進(jìn)行了包裝:

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
}

如果有設(shè)置JSONIndent, 即JSON縮進(jìn),那么使用json.MarshalIndent來(lái)將變量轉(zhuǎn)換為json字節(jié)流。 這個(gè)方法其實(shí)就是將JSON格式化,使得結(jié)果看起來(lái)更好看。

另外這個(gè)方法還會(huì)根據(jù)配置,進(jìn)行html實(shí)體的轉(zhuǎn)義。

因此總體來(lái)說(shuō)原理和開頭的代碼基本一樣。只不過(guò)多了一些額外的修飾修補(bǔ)。

JSONP方法

我們理解了JSON方法,理解起JSONP就更加簡(jiǎn)單了。

JSONP全稱為JSON with Padding, 用于解決Ajax跨域問(wèn)題的一種方案。

它的原理非常簡(jiǎn)單:

// 客戶端代碼
var dosomething = function(data) {
  // do something with data
}

var url = "server.jsonp?callback=dosomething";
 // 創(chuàng)建 script> 標(biāo)簽,設(shè)置其 src 屬性
 var script = document.createElement('script');
 script.setAttribute('src', url);

 // 把 script> 標(biāo)簽加入 body> 尾部,此時(shí)調(diào)用開始。
 document.getElementsByTagName('body')[0].appendChild(script);
上面server.jsonp是一個(gè)后臺(tái)腳本,訪問(wèn)后立即返回它的輸出內(nèi)容, 這也就是renderer的JSONP要響應(yīng)的內(nèi)容。

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. 設(shè)置Content-Type為application/javascript, 非常關(guān)鍵的一點(diǎn)。 想一想html中嵌入的js文件的mime類型是不是也是這個(gè)值?
  2. 然后同樣的設(shè)置響應(yīng)狀態(tài)碼, 這點(diǎn)沒(méi)有什么特殊的。
  3. 將值轉(zhuǎn)換為json字節(jié)序列。這個(gè)json字節(jié)序列還沒(méi)有向響應(yīng)寫入進(jìn)去。
  4. 這個(gè)時(shí)候我們檢查callback是否存在,不存在報(bào)錯(cuò)出去。因?yàn)槭荍SONP, 必須要有callback, 這個(gè)callback是請(qǐng)求參數(shù)傳入的。
  5. 然后用"callbak("和")"將json字節(jié)序列包圍起來(lái),一起輸出到響應(yīng)流中。這樣jsonp響應(yīng)就產(chǎn)生了。

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

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

這樣服務(wù)端產(chǎn)生數(shù)據(jù),并調(diào)用前端js的方法, 傳入這些數(shù)據(jù), jsonp就完成了。這樣的js一旦加載成功,它和當(dāng)前訪問(wèn)域名是同源的,不存在跨域問(wèn)題。 這樣就解決了ajax跨域問(wèn)題。

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

本文僅個(gè)人學(xué)習(xí)整理, 如有不對(duì)之處, 還望各位不吝指出。

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

引用鏈接

  1. renderer
  2. Go In Action

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

標(biāo)簽:阿壩 駐馬店 瀘州 泰安 東營(yíng) 昭通 晉中 滄州

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《淺談go語(yǔ)言renderer包代碼分析》,本文關(guān)鍵詞  淺談,語(yǔ)言,renderer,包代碼,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《淺談go語(yǔ)言renderer包代碼分析》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于淺談go語(yǔ)言renderer包代碼分析的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    国产.精品.日韩.另类.中文.在线.播放| 中文字幕一区二区三区乱码在线 | 精品福利一区二区三区免费视频| 欧美视频在线不卡| 欧美国产精品专区| 久久人人爽爽爽人久久久| 麻豆91在线观看| 色婷婷亚洲一区二区三区| 亚洲综合在线第一页| 中文在线一区二区| 在线观看亚洲精品视频| 91在线免费视频观看| 中文字幕一区二区在线播放| 成人三级在线视频| 久久久美女艺术照精彩视频福利播放 | 日一区二区三区| 91免费观看在线| 一区二区三区国产| 91在线视频网址| 久久99久国产精品黄毛片色诱| 中文字幕一区三区| voyeur盗摄精品| 亚洲图片欧美视频| 欧美视频一区二区三区四区| 中文字幕 久热精品 视频在线| 亚洲高清免费一级二级三级| 欧美成人精品福利| 欧美性大战久久久| 91在线观看高清| 亚洲综合免费观看高清完整版在线| 国产一区二区三区四区五区美女 | 蜜臀国产一区二区三区在线播放| 久久综合久色欧美综合狠狠| 欧美xxxx老人做受| 91在线视频18| 美洲天堂一区二卡三卡四卡视频| 精品国产亚洲一区二区三区在线观看| 蜜臀精品久久久久久蜜臀| 亚洲天堂精品在线观看| 欧美经典三级视频一区二区三区| 国产一区二区三区免费播放| 国产精品一区免费视频| 老司机精品视频一区二区三区| 国产精品久久久久久亚洲伦| 欧美午夜精品一区二区蜜桃| 欧美一区二区日韩| 99久久精品国产一区| 麻豆视频观看网址久久| 91精品一区二区三区在线观看| 韩日av一区二区| 久久色在线观看| 91精品黄色片免费大全| 91黄色激情网站| 黄色成人免费在线| 麻豆精品在线播放| 精品亚洲国产成人av制服丝袜| k8久久久一区二区三区| 色婷婷av一区| 久久97超碰色| 成人三级在线视频| 欧美午夜精品理论片a级按摩| 国产毛片一区二区| 麻豆国产一区二区| 亚洲线精品一区二区三区八戒| 亚洲国产视频网站| 日韩一区精品视频| 国产91精品在线观看| 青青青伊人色综合久久| 亚洲6080在线| 久色婷婷小香蕉久久| 亚洲高清免费视频| 99riav久久精品riav| 一本一本大道香蕉久在线精品| 久久伊人蜜桃av一区二区| 中文成人av在线| 韩国av一区二区| 91女人视频在线观看| 在线观看日产精品| 日韩精品一区二区三区在线播放| 精品国产凹凸成av人导航| 欧美性生活久久| 91精品黄色片免费大全| 国产偷v国产偷v亚洲高清| 91精品国产综合久久久蜜臀粉嫩 | 日韩一区二区三区观看| 国产欧美日韩不卡| 日韩有码一区二区三区| 欧美日韩国产首页| 亚洲欧美一区二区三区久本道91 | 亚洲国产精品成人综合色在线婷婷| 亚洲一区二区在线视频| 亚洲乱码一区二区三区在线观看| 色噜噜狠狠成人网p站| 国产色婷婷亚洲99精品小说| 在线观看精品一区| 成人精品gif动图一区| 中文字幕欧美区| 国产成人在线视频免费播放| 日韩三区在线观看| 婷婷激情综合网| 国产精品少妇自拍| 99久久99久久精品免费看蜜桃| 奇米色777欧美一区二区| 国产午夜精品美女毛片视频| 日本丰满少妇一区二区三区| 五月综合激情网| 亚洲国产精品成人综合| 成人av先锋影音| 久久国产精品一区二区| 久久综合国产精品| 欧美午夜一区二区三区免费大片| 国产精品99久久久久久久vr| 一区二区三区中文在线观看| 欧美日韩国产a| 国产精品一区二区三区99| 国产精品欧美一区喷水| 精品一区二区精品| 夜夜嗨av一区二区三区四季av| 久久蜜臀精品av| 337p亚洲精品色噜噜| 色悠悠久久综合| 处破女av一区二区| 香蕉久久夜色精品国产使用方法| 久久综合色鬼综合色| 国产精品第五页| 综合久久综合久久| 一区二区三区日本| 亚洲欧美一区二区在线观看| 久久免费精品国产久精品久久久久 | 不卡的电影网站| 日本欧美加勒比视频| 综合欧美亚洲日本| 欧美区视频在线观看| 亚洲精品视频一区| 国产亚洲欧洲一区高清在线观看| 在线日韩一区二区| 亚洲国产视频在线| 一区二区三区精品| 伊人性伊人情综合网| 视频一区二区三区入口| 婷婷综合久久一区二区三区| 亚洲综合激情网| 一区二区成人在线| 精品无人码麻豆乱码1区2区 | 国产精品天干天干在观线| 久久亚洲精精品中文字幕早川悠里 | 美女网站色91| 国产精品一区二区91| 国产一区激情在线| 色网综合在线观看| 欧美精品免费视频| 欧美激情资源网| 亚洲成人av在线电影| 91久久免费观看| 久久蜜桃一区二区| 精品视频一区三区九区| 欧美日韩日日摸| 一区二区在线观看不卡| 色域天天综合网| 1区2区3区精品视频| 久久99最新地址| 精品视频色一区| 亚洲精品久久嫩草网站秘色| 日韩一区精品字幕| 91精品国产入口在线| 亚洲成人免费观看| 亚洲精品中文字幕在线观看| 成人精品小蝌蚪| 国产日韩成人精品| 国产精品一区二区免费不卡| 欧美在线综合视频| 亚洲成人动漫av| 成人午夜激情片| 欧美v日韩v国产v| 国产综合色视频| 中文字幕一区二区不卡| www.欧美日韩| 中文字幕在线一区| 国产精品对白交换视频| 粉嫩aⅴ一区二区三区四区五区| 国产精品污www在线观看| 成人av高清在线| 国产欧美久久久精品影院| 国产又黄又大久久| 国产精品久久久99| 欧美一区二区三区不卡| 北条麻妃一区二区三区| 亚洲一区二区三区四区五区中文 | 欧美成人a在线| 国产二区国产一区在线观看| 精品国产乱码久久久久久老虎 | 亚洲色图欧美激情| 精品国产乱码久久| 97精品电影院| 日韩国产欧美视频| 久久久久久久国产精品影院| 欧美亚洲国产bt| 国产精品77777竹菊影视小说| 午夜精品福利一区二区蜜股av | 亚洲激情校园春色|