整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript的賦值、深拷貝和淺拷貝

          JavaScript的賦值、深拷貝和淺拷貝

          存空間

          在JavaScript中,每一個數據都需要一個內存空間。內存空間分為兩種,棧內存(stack)與堆內存(heap)

          棧是系統自動分配的內存空間,由系統自動釋放,堆則是動態分配的內存,大小不定不會自動釋放。

          基本數據類型

          JavaScript中的基本數據類型,這些值都有固定的大小,保存在內存中,由系統自動分配存儲空間在棧內存空間的值,我們可以直接進行操作,因此基礎數據類型都是按照值訪問

          在棧內存中的數據發生復制的行為時,系統會自動為新變量開辟一個新的內存空間,當復制執行后,兩個內存空間的值就互不影響,改變其中一個不會影響另一個

          var a=`I am variable a`;
          var b=a; 
          console.log(b); //`I am variable a`
          b=`I am variable b`;
          console.log(a); //`I am variable a`
          console.log(b); //`I am variable b`
          

          引用數據類型

          引用類型的值是保存在內存中的對象,在JavaScript中我們不能直接操作對象的堆內存空間。因為引用類型的值都是按引用訪問的,所以在操作對象時,實際上是操作對象的引用而不是實際的對象。引用可以理解為保存在棧內存中的一個地址,該地址指向堆內存中的一個實際對象

          引用類型值的復制,系統會為新的變量自動分配一個新的棧內存空間這個棧內存空間保存著與被復制變量相同的指針,盡管他們在棧內存中的內存空間的位置互相獨立但是在堆內存中訪問到的對象實際上是同一個,因此,當我們改變其中一個對象的值時,實際上就是改變原來的對象

          棧內存空間保存指針(地址),堆內存空間保存實際的對象,我們通過變量訪問對象時,實際上訪問的是對象的引用(地址)

          內存中的棧區域存放變量(基本類型的變量包括變量聲明和值)以及指向堆區域存儲位置的指針(引用類型的變量包括變量聲明和指向內容的指針)

          var a={
           name : `I am object a`,
           type : 'object'
          }
          var b=a;
          console.log(b);
          // {name: "I am object a", type: "object"}
          b.name=`I am object b`;
          console.log(a);
          // {name: "I am object b", type: "object"}
          console.log(b);
          // {name: "I am object b", type: "object"}
          

          基本類型總結

          基本數據類型:

          包括:null、undefined、number、string、boolean、symbol(es6)

          存放位置:內存中的棧區域中

          比較:值的比較,判斷是否相等,如果值相等,就相等。一般使用===進行比較,因為==會進行類型的轉換

          拷貝:賦值(通過(=)賦值操作符 賦值),賦值完成后,兩個變量之間就沒有任何關系了,改變其中一個變量的值對另一個沒有任何影響

          引用類型總結

          引用數據類型:

          包括:數組、對象、函數

          存放位置:內存的棧區域中存放變量和指針,堆區域存儲實際的對象

          比較:是引用的比較(就是地址的比較,變量在棧內存中對應的指針地址相等就指向同一個對象)判斷是否為同一個對象,示例如下

          變量a和變量b的引用不同,對象就不是同一個對象
          var a={name:'Jay'};
          var b={name:'Jay'};
          a===b //false
          

          我們對JavaScript中引用類型進行操作的時候,都是操作其對象的引用(保存在棧內存中的指針)

          賦值、深拷貝和淺拷貝 (Assignment, deep copy and shallow copy)

          賦值:兩個變量的值(指針)都指向同一個對象,改變其中一個,另一個也會受到影響

          所謂拷貝就是復制,通過復制原對象生成一個新的對象

          淺拷貝:重新在堆內存中開辟一個空間,拷貝后新對象獲得一個獨立的基本數據類型數據,和原對象共用一個原對象內的引用類型數據,改變基本類型數據,兩個對象互不影響,改變其中一個對象內的引用類型數據,另一個對象會受到影響

          var obj={
           name: 'Jay Chou',
           age: 32,
           song:{
           name:'菊花臺',
           year:2007
           }
          }
          var obj1=obj;
          function shallowCopy(obj){
           if(!obj || typeof obj !=='object'){
           return ;
           }
           var scObj=Array.isArray(obj) ? [] : {};
           //var scObj=(obj instanceof Array) ? [] :{};
           //var scObj=obj.constructor===Array ? [] : {};
           for(var prop in obj){
           if(obj.hasOwnProperty(prop)){
           scObj[prop]=obj[prop]
           }
           }
           return scObj;
          }
          var obj2=shallowCopy(obj);
          console.log(obj===obj1,'obj===obj1','賦值');
          console.log(obj===obj2,'obj===obj2','淺拷貝');
          // true "obj===obj1" "賦值"
          // false "obj===obj2" "淺拷貝"
          console.log(obj.song===obj2.song);
          //true
          obj2.song.name='菊花臺';
          obj2.name='Jay';
          console.log(obj)
          // {name: "Jay Chou", age: 32, song: {name:'菊花臺',year:2007}}
          console.log(obj1);
          // {name: "Jay Chou", age: 32, song: {name:'菊花臺',year:2007}}
          console.log(obj2);
          {name: "Jay", age: 32, song: {name:'菊花臺',year:2007}}
          console.log(obj===obj1)
          //true
          console.log(obj===obj2)
          //false
          

          深拷貝:不論是對象內的基本類型還是引用類型都被完全拷貝,拷貝后兩個對象互不影響

          一種比較簡單實現方法是使用 var dataObj=JSON.parse(JSON.stringify(data))

          var obj={
           name: 'Jay Chou',
           age: 32,
           song:{
           name:'菊花臺',
           year:2007
           }
          }
          var dcObj=JSON.parse(JSON.stringify(obj));
          console.log(dcObj);
          // {name: "Jay Chou", age: 32, song: {name:'菊花臺',year:2007}}
          console.log(dcObj.song===obj.song);
          //false
          dcObj.name='Jay';
          dcObj.song.name='雙截棍';
          console.log(obj);
          // {name: "Jay Chou", age: 32, song: {name:'菊花臺',year:2007}}
          console.log(dcObj);
          //{name: "Jay", age: 32, song: {name:'雙截棍',year:2007}}
          

          需要注意的是,使用JSON.Stringify()序列化對象時會把對象內的function和原型成員忽略掉,示例如下

          var obj={
           name: 'Jay Chou',
           job: 'artist',
           say:function(){
           alert(this.job);
           }
          }
          JSON.stringify(obj);
          //"{"name":"Jay Chou","job":"artist"}"
          

          通過遞歸淺拷貝函數實現深拷貝

          function deepCopy(obj){
           if(!obj || typeof obj !=='object'){
           return ;
           }
           var dcObj=Array.isArray(obj) ? [] : {};
           for(var key in obj){
           if(obj.hasOwnProperty(key)){
           if(obj[key] && typeof obj[key]==='object'){
           dcObj[key]=Array.isArray(obj[key]) ? [] : {};
           dcObj[key]=deepCopy(obj[key]);
           }
           dcObj[key]=obj[key]
           }
           }
           return dcObj;
          }
          

          比較:賦值、深拷貝、淺拷貝

          賦值: 變量獲得原對象的引用,改變該引用指向的對象的值(基本類型和引用類型)其實就是修改原對象的值

          淺拷貝: 改變新對象基本類型的值不會使原對象對應的值一起改變,但是改變新對象引用類型的值會使原對象對應的值一同改變

          深拷貝: 改變新對象基本類型和引用類型的值,都不會影響原對象,兩者互相獨立,互不影響

          SON,全稱為 JavaScript Object Notation, 也就是 JavaScript 對象標記,它通過對象和數組的組合來表示數據,構造簡潔但是結構化程度非常高,是一種輕量級的數據交換格式。

          本節中,我們就來了解如何利用 Python 保存數據到 JSON 文件。

          1. 對象和數組

          在 JavaScript 語言中,一切都是對象。因此,任何支持的類型都可以通過 JSON 來表示,例如字符串、數字、對象、數組等,但是對象和數組是比較特殊且常用的兩種類型,下面簡要介紹一下它們。

          • 對象:它在 JavaScript 中是使用花括號 {} 包裹起來的內容,數據結構為 {key1:value1, key2:value2, ...} 的鍵值對結構。在面向對象的語言中,key 為對象的屬性,value 為對應的值。鍵名可以使用整數和字符串來表示。值的類型可以是任意類型。
          • 數組:數組在 JavaScript 中是方括號 [] 包裹起來的內容,數據結構為 ["java", "javascript", "vb", ...] 的索引結構。在 JavaScript 中,數組是一種比較特殊的數據類型,它也可以像對象那樣使用鍵值對,但還是索引用得多。同樣,值的類型可以是任意類型。

          所以,一個 JSON 對象可以寫為如下形式:

          [
            {
              name: "Bob",
              gender: "male",
              birthday: "1992-10-18",
            },
            {
              name: "Selina",
              gender: "female",
              birthday: "1995-10-18",
            },
          ];

          由中括號包圍的就相當于列表類型,列表中的每個元素可以是任意類型,這個示例中它是字典類型,由大括號包圍。

          JSON 可以由以上兩種形式自由組合而成,可以無限次嵌套,結構清晰,是數據交換的極佳方式。

          2. 讀取 JSON

          Python 為我們提供了簡單易用的 JSON 庫來實現 JSON 文件的讀寫操作,我們可以調用 JSON 庫的 loads 方法將 JSON 文本字符串轉為 JSON 對象,實際上 JSON 對象為 Python 中的 list 和 dict 的嵌套和組合,這里稱之為 JSON 對象。另外我們還可以通過 dumps 方法將 JSON 對象轉為文本字符串。

          例如,這里有一段 JSON 形式的字符串,它是 str 類型,我們用 Python 將其轉換為可操作的數據結構,如列表或字典:

          import json
          
          str='''
          [{
              "name": "Bob",
              "gender": "male",
              "birthday": "1992-10-18"
          }, {
              "name": "Selina",
              "gender": "female",
              "birthday": "1995-10-18"
          }]
          '''
          print(type(str))
          data=json.loads(str)
          print(data)
          print(type(data))

          運行結果如下:

          <class'str'>
          [{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-18'}, {'name': 'Selina', 'gender': 'female', 'birthday': '1995-10-18'}]
          <class 'list'>

          這里使用 loads 方法將字符串轉為 JSON 對象。由于最外層是中括號,所以最終的類型是列表類型。

          這樣一來,我們就可以用索引來獲取對應的內容了。例如,如果想取第一個元素里的 name 屬性,就可以使用如下方式:

          data[0]['name']
          data[0].get('name')

          得到的結果都是:

          Bob

          通過中括號加 0 索引,可以得到第一個字典元素,然后再調用其鍵名即可得到相應的鍵值。獲取鍵值時有兩種方式,一種是中括號加鍵名,另一種是通過 get 方法傳入鍵名。這里推薦使用 get 方法,這樣如果鍵名不存在,則不會報錯,會返回 None。另外,get 方法還可以傳入第二個參數(即默認值),示例如下:

          data[0].get('age')
          data[0].get('age', 25)

          運行結果如下:

          None
          25

          這里我們嘗試獲取年齡 age,其實在原字典中該鍵名不存在,此時默認會返回 None。如果傳入第二個參數(即默認值),那么在不存在的情況下返回該默認值。

          值得注意的是,JSON 的數據需要用雙引號來包圍,不能使用單引號。例如,若使用如下形式表示,則會出現錯誤:

          import json
          
          str='''
          [{
              'name': 'Bob',
              'gender': 'male',
              'birthday': '1992-10-18'
          }]
          '''
          data=json.loads(str)

          運行結果如下:

          json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 3 column 5 (char 8)

          這里會出現 JSON 解析錯誤的提示。這是因為這里數據用單引號來包圍,請千萬注意 JSON 字符串的表示需要用雙引號,否則 loads 方法會解析失敗。

          如果從 JSON 文本中讀取內容,例如這里有一個 data.json 文本文件,其內容是剛才定義的 JSON 字符串,我們可以先將文本文件內容讀出,然后再利用 loads 方法轉化:

          import json
          
          with open('data.json', encoding='utf-8') as file:
              str=file.read()
              data=json.loads(str)
              print(data)

          運行結果如下:

          [{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-18'}, {'name': 'Selina', 'gender': 'female', 'birthday': '1995-10-18'}]

          這里我們用 open 方法讀取了文本文件,同時使用了默認的讀模式,編碼指定為 utf-8,文件操作對象賦值為 file。接著我們調用了 file 對象的 read 方法讀取了文本的所有內容,賦值為 str。然后再調用 loads 方法解析 JSON 字符串,將其轉化為 JSON 對象。

          這里其實也有更簡便的寫法,我們可以直接使用 load 方法傳入文件操作對象,同樣也可以將文本轉化為 JSON 對象,寫法如下:

          import json
          
          data=json.load(open('data.json', encoding='utf-8'))
          print(data)

          注意這里使用的是 load 方法,而不是 loads 方法。前者的參數是一個文件操作對象,后者的參數是一個 JSON 字符串。

          這兩種寫法的運行結果也是完全一樣的。只不過 load 方法是將整個文件的內容轉化為 JSON 對象,而使用 loads 方法可以更靈活地控制要轉化的內容。兩種方法可以在適當的場景下使用。

          3. 輸出 JSON

          另外,我們還可以調用 dumps 方法將 JSON 對象轉化為字符串。例如,將上例中的列表重新寫入文本:

          import json
          
          data=[{
              'name': 'Bob',
              'gender': 'male',
              'birthday': '1992-10-18'
          }]
          with open('data.json', 'w', encoding='utf-8') as file:
              file.write(json.dumps(data))

          利用 dumps 方法,我們可以將 JSON 對象轉為字符串,然后再調用文件的 write 方法寫入文本,結果如圖所示。

          另外,如果想保存 JSON 的格式縮進,可以再加一個參數 indent,代表縮進字符個數。示例如下:

          with open('data.json', 'w') as file:
              file.write(json.dumps(data, indent=2))

          此時寫入結果如圖所示。

          這樣得到的內容會自動帶縮進,格式會更加清晰。

          另外,如果 JSON 中包含中文字符,會怎么樣呢?例如,我們將之前的 JSON 的部分值改為中文,再用之前的方法寫入到文本:

          import json
          
          data=[{
              'name': '王偉',
              'gender': '男',
              'birthday': '1992-10-18'
          }]
          with open('data.json', 'w', encoding='utf-8') as file:
              file.write(json.dumps(data, indent=2))

          寫入結果如圖所示。

          可以看到,中文字符都變成了 Unicode 字符,這并不是我們想要的結果。

          為了輸出中文,還需要指定參數 ensure_ascii 為 False,另外還要規定文件輸出的編碼:

          with open('data.json', 'w', encoding='utf-8') as file:
              file.write(json.dumps(data, indent=2, ensure_ascii=False))

          寫入結果如圖所示。

          可以發現,這樣就可以輸出 JSON 為中文了。

          同樣地,類比 loads 與 load 方法,dumps 也有對應的 dump 方法,它可以直接將 JSON 對象全部寫入到文件中,因此上述的寫法也可以寫為如下形式:

          json.dump(data, open('data.json', 'w', encoding='utf-8'), indent=2, ensure_ascii=False)

          這里第一個參數就是 JSON 對象,第二個參數可以傳入文件操作對象,其他的 indent、ensure_ascii 對象還是保持不變,運行效果是一樣的。

          4. 總結

          本節中,我們了解了用 Python 進行 JSON 文件讀寫的方法,后面做數據解析時經常會用到,建議熟練掌握。

          本節代碼:https://github.com/Python3WebSpider/FileStorageTest。

          開始分析之前,我們先簡單回顧一下上一個章節中講到的Gin框架中的幾個核心的結構.

          Gin框架中的幾個核心結構

          go語言中文文檔:www.topgoer.com

          轉自:https://www.jianshu.com/p/9d1886b70ed9

          Gin框架中的幾個重要的模型:

          • Engine: 用來初始化一個gin對象實例,在該對象實例中主要包含了一些框架的基礎功能,比如日志,中間件設置,路由控制(組),以及handlercontext等相關方法.源碼文件
          • Router: 用來定義各種路由規則和條件,并通過HTTP服務將具體的路由注冊到一個由context實現的handler中
          • Context: Context是框架中非常重要的一點,它允許我們在中間件間共享變量,管理整個流程,驗證請求的json以及提供一個json的響應體. 通常情況下我們的業務邏輯處理也是在整個Context引用對象中進行實現的.
          • Bind: 在Context中我們已經可以獲取到請求的詳細信息,比如HTTP請求頭和請求體,但是我們需要根據不同的HTTP協議參數來獲取相應的格式化
            數據來處理底層的業務邏輯,就需要使用Bind相關的結構方法來解析context中的HTTP數據

          1.Gin框架對HTTP響應數據的處理

          我們在深入Gin框架內幕(一)中,以一個簡單的Gin實例來具體講解它內部是如何創建一個Http服務,并且注冊一個路由來接收用戶的請求,在示例程序中我們使用了Context引用對象的String方法來處理HTTP服務的數據響應,所以在整個Gin框架中緊跟Router模型結構的就要屬Context結構了,該結構體主要用來處理整個HTTP請求的上下文數據,也是我們在開發HTTP服務中相對比較重要的一個結構體了。

          # 深入Gin框架內幕(一)中的示例
          $ cat case1.go
          package main
          import (
              "net/http"
          
              "github.com/gin-gonic/gin"
          )
          func main() {
              ginObj :=gin.Default()
              ginObj.Any("/hello",func(c *gin.Context){
                  c.String(http.StatusOK,"Hello BGBiao.")
              })
              ginObj.Run("localhost:8080")
          }
          

          我們可以看到,在使用Gin框架后,我們只需要很簡單的代碼,即可以快速運行一個返回Hello BGBiao.的HTTP服務,而在ginObj.Any方法中,我們傳入了一個參數為Context引用類型的匿名函數,并在該函數內部采用String(code,data)方法來處理HTTP服務的響應數據(返回Hello BGBiao字符串),這個時候,你可能會想,我們在企業內部都是前后端分離,通常情況下后端僅會提供RESTful API,并通過JSON格式的數據和前端進行交互,那么Gin是如何處理其他非字符串類型的數據響應呢,這也是我們接下來要主要講的Context結構模型。

          2.Gin框架中的Context結構體

          注意: 在Gin框架中由Router結構體來負責路由和方法(URL和HTTP方法)的綁定,內的Handler采用Context結構體來處理具體的HTTP數據傳輸方式,比如HTTP頭部,請求體參數,狀態碼以及響應體和其他的一些常見HTTP行為。

          Context結構體:

          type Context struct {
              // 一個包含size,status和ResponseWriter的結構體
              writermem responseWriter
              // http的請求體(指向原生的http.Request指針)
              Request   *http.Request
              // ResonseWriter接口
              Writer    ResponseWriter
          
              // 請求參數[]{"Key":"Value"}
              Params   Params
              handlers HandlersChain
              index int8
              // http請求的全路徑地址
              fullPath string
              // gin框架的Engine結構體指針
              engine   *Engine
              // 每個請求的context中的唯一鍵值對
              Keys map[string]interface{}
              // 綁定到所有使用該context的handler/middlewares的錯誤列表
              Errors errorMsgs
              // 定義了允許的格式被用于內容協商(content)
              Accepted []string
              // queryCache 使用url.ParseQuery來緩存參數查詢結果(c.Request.URL.Query())
              queryCache url.Values
              // formCache 使用url.ParseQuery來緩存PostForm包含的表單數據(來自POST,PATCH,PUT請求體參數)
              formCache url.Values
          }
          

          Context結構體常用的一些方法

          基本方法:

          • Copy(): 返回當前正在使用的context的拷貝(context指針),當這個context必須在goroutine中用時,該方法比較有用
          • HandlerName(): 返回當前主handler的名稱(比如:handler為handleGetUsers(),該方法將返回"main.handleGetUsers")
          • HandlerNames(): 返回所有注冊的handler的名稱
          • Handler(): 返回當前的主handler(func (c *Context) Handler() HandlerFunc)
          • FullPath(): 返回一個匹配路由的全路徑(uri: "/user/:id",c.FullPath()=="/user/:id" )

          http常用方法:

          • ClientIP() string: 返回客戶端ip(該方法會解析X-Real-IP,X-Forwarded-For)
          • ContentType() string: 返回HTTP的Content-Type頭
          • IsWebsocket() bool: 返回是否為ws鏈接

          流控相關的方法:

          • Next(): 該方法僅被使用在middleware中,它會在被調用的handler鏈內部執行pending handler
          • IsAborted(): 如果當前的context被終止了,該方法返回true
          • Abort(): 該函數可以從正在被調用中保護pending handler. 該方法停止后不會停止當前正在執行的handler. 比如我們有一個鑒權的中間件來驗證請求是否有權限,如果認證失敗了(用戶信息異常等),此時調用Abort()來確保后面的handler不再被調用
          • AbortWithStatus(code int): 同上,在會寫入狀態碼。context.AbortWithStatus(401)即可表示上述的鑒權失敗
          • AbortWithStatusJSON(code int, jsonObj interface{}): 同上,會再加響應數據.該方法會停止整個handler鏈,再寫入狀態碼和json的響應體,同時也會設置Content-Type="application/json"
          • AbortWithError(code int, err error) *Error: 同上返回錯誤信息

          錯誤管理:

          • Error(err error) *Error: 返回一些錯誤對象

          元數據管理:

          • Set(key string, value interface{}): 給當前這個context設置一個新的鍵值對
          • Get(key string) (value interface{}, exists bool): 返回指定的key的值,以及是否存在
          • MustGet(key string) interface{}: 返回指定key的值,不存在則panic
          • GetString(key string) (s string): 以string類型返回指定的key
          • GetBool(key string) (b bool): 返回分配給該key的值(bool類型)
          • GetInt(key string) (i int):
          • GetStringSlice(key string) (ss []string): 返回key的slice類型
          • GetStringMap(key string) (sm map[string]interface{}): 返回interface{}類型的map結構
          • GetStringMapString(key string) (sms map[string]string): 返回string類型的map結構
          • GetStringMapStringSlice(key string) (smss map[string][]string): 同理

          輸入數據:

          • Param(key string) string: 返回URL的參數值(uri_patten: "/user/:id",url: "/user/john",c.Param("id")="john")
          • Query(key string) string: 返回url中的查詢參數值(url: "/path?id=1234&name=Manu&value=",c.Query("id")為1234,c.Query("name")為Manu,c.Query("value")為空)
          • DefaultQuery(key, defaultValue string) string: 返回url中的查詢參數的默認值(同上,但是c.Query("value")就沒有值,該方法可以設置默認值)
          • GetQuery(key string) (string, bool): 同Query()方法,并且會返回狀態,如果對應的key不存在,返回("",false)
          • QueryArray(key string) []string: 返回指定key的對應的array(slice的長度取決于給定key的參數的數量)
          • GetQueryArray(key string) ([]string, bool): 同上,會返回狀態
          • QueryMap(key string) map[string]string: 返回指定key對應map類型
          • GetQueryMap(key string) (map[string]string, bool): 同上,并且會返回狀態
          • PostForm(key string) string: 該方法返回一個從POST 請求的urlencode表單或者multipart表單數據,不存在時返回空字符串
          • DefaultPostForm(key, defaultValue string) string: 同上,key不存在時返回默認值
          • GetPostForm(key string) (string, bool): 同PostForm()方法,并且會返回狀態
          • PostFormArray(key string) []string: 該方法返回指定key的字符串類型的slice
          • GetPostFormArray(key string) ([]string, bool): 同上,并返回狀態
          • PostFormMap(key string) map[string]string: 返回指定key的map類型
          • GetPostFormMap(key string) (map[string]string, bool): 同上,并返回狀態
          • FormFile(name string) (*multipart.FileHeader, error): 返回指定key的第一個文件(用作文件上傳)
          • MultipartForm() (*multipart.Form, error): 該方法解析multipart表單,包含file文件上傳
          • SaveUploadedFile(file *multipart.FileHeader, dst string) error: 該方法用來上傳指定的文件頭到目標路徑(dst)

          Bind家族相關方法:

          • Bind(obj interface{}) error: 自動解析Content-Type并綁定到指定的binding引擎
          • BindJSON(obj interface{}) error: 同上,binding引擎為binding.JSON
          • BindXML(obj interface{}) error:
          • BindQuery(obj interface{}) error:
          • BindYAML(obj interface{}) error:
          • BindHeader(obj interface{}) error:
          • BindUri(obj interface{}) error: 使用binding.Uri來綁定傳遞的結構體指針
          • MustBindWith(obj interface{}, b binding.Binding) error: 使用指定的binding引擎來綁定傳遞的結構體指針(當有任何錯誤時,終止請求并返回400)

          ShouldBind家族相關方法:

          • ShouldBind(obj interface{}) error: 同上述的Bind()方法,但是該方法在json結構無效時不會返回400
          • ShouldBindJSON(obj interface{}) error:
          • ShouldBindXML(obj interface{}) error:
          • ShouldBindQuery(obj interface{}) error:
          • ShouldBindYAML(obj interface{}) error:
          • ShouldBindHeader(obj interface{}) error:
          • ShouldBindUri(obj interface{}) error:
          • ShouldBindWith(obj interface{}, b binding.Binding) error: 等同于MustBindWith()方法
          • ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error): 和ShouldBindWith()方法相似,但是他會存儲請求體到context中,當下次調用時可以重用(因為該方法是在binding之前讀取body,因此在你只使用一次時,為了更好的性能還是使用ShouldBindWith會比較好)

          HTTP響應相關的方法:

          • Status(code int): 設置http的響應碼
          • Header(key, value string): 是c.Writer.Header().Set(key, value)的簡單實現,在響應體重寫入一個header,如果value為空,則相當于調用了c.Writer.Header().Del(key)
          • GetHeader(key string) string: 返回請求體重的header
          • GetRawData() ([]byte, error): 返回流式數據
          • SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool): 該方法將設置一個Set-Cookie到ResponseWriter的頭中(注意:name必須是一個合法可用的名稱,無效的coookie可能會被丟棄)
          • Cookie(name string) (string, error): 返回名稱為name的cookie
          • Render(code int, r render.Render): 該方法寫入響應頭并調用render.Render去渲染數據
          • HTML(code int, name string, obj interface{}): 該方法使用指定文件模板名稱去渲染http模板(同時會更新狀態碼并設置Content-Type as "text/html".)
          • IndentedJSON(code int, obj interface{}): 該方法會序列化對象obj為一個pretty JSON 數據到響應體中,同時設置Content-Type as "application/json"(pretty JSON需要消耗cpu和帶寬,強烈建議生產使用Context.JSON())
          • SecureJSON(code int, obj interface{}): 同上,會序列化成Secure Json
          • JSONP(code int, obj interface{}):
          • JSON(code int, obj interface{}): 序列化為JSON,并寫Content-Type:"application/json"頭
          • AsciiJSON(code int, obj interface{}):
          • PureJSON(code int, obj interface{}):
          • XML(code int, obj interface{}): 序列化成xml格式,并寫Content-Type:"application/xml"
          • YAML(code int, obj interface{}): 序列化成yaml
          • ProtoBuf(code int, obj interface{}): 序列化成probuf
          • String(code int, format string, values ...interface{}): 將制定的string寫入響應體
          • Redirect(code int, location string): 重定向
          • Data(code int, contentType string, data []byte): 寫一些數據到響應體重,并更新響應碼
          • DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string): 寫一些制定模板的數據到響應體中,并更新狀態碼
          • File(filepath string): 以一種高效方式將制定文件寫入響應體數據中
          • FileAttachment(filepath, filename string): 同上,但是在客戶端文件會被直接下載下來
          • SSEvent(name string, message interface{}): 寫Server-Sent Event到響應數據中
          • Stream(step func(w io.Writer) bool) bool: 發送一個流式的響應數據并返回狀態

          3.Gin實例示例

          3.1返回json格式的數據

          為了解決我們在開頭提到的問題,我們將使用context引用對象的JSON家族方法來處理該需求

          # 使用context來返回json格式的數據
          $ cat case2.go
          package main
          
          import (
              "github.com/gin-gonic/gin"
          )
          
          // 我們定義一個通用的格式化的響應數據
          // 在Data字段中采用空接口類型來實際存放我們的業務數據
          type restData struct {
              Data        interface{}   `json:"data"`
              Message     string        `json:"message"`
              Status      bool          `json:"status"`
          }
          
          func main() {
              // mock一個http響應數據
              restdata :=&restData{"Hello,BGBiao","",true}
              restdata1 :=&restData{map[string]string{"name":"BGBiao","website":"https://bgbiao.top"},"",true}
          
              // 使用Gin框架啟動一個http接口服務
              ginObj :=gin.Default()
              ginObj.GET("/api/test",func(c *gin.Context){
                 // 我們的handlerFunc中入參是一個Context結構的引用對象c
                 // 因此我們可以使用Context中的JSON方法來返回一個json結構的數據
                 // 可用的方法有如下幾種,我們可以根據實際需求進行選擇
                 /*
                    IndentedJSON(code int, obj interface{}): 帶縮進的json(消耗cpu和mem)
                    SecureJSON(code int, obj interface{}): 安全化json
                    JSONP(code int, obj interface{})
                    JSON(code int, obj interface{}): 序列化為JSON,并寫Content-Type:"application/json"頭
                 */
                 c.JSON(200,restdata)
              })
              ginObj.GET("/api/test1",func(c *gin.Context){
                  c.IndentedJSON(200,restdata1)
              })
          
              ginObj.Run("localhost:8080")
          }
          
          
          # 實例運行(這里成功將我們寫的兩個api接口進行對外暴露)
          $ go run case2.go
          ....
          ....
          [GIN-debug] GET    /api/test                 --> main.main.func1 (3 handlers)
          [GIN-debug] GET    /api/test1                --> main.main.func2 (3 handlers)
          
          # 接口測試訪問
          $ curl localhost:8080/api/test
          {"data":"Hello,BGBiao","message":"","status":true}
          $ curl localhost:8080/api/test1
          {
              "data": {
                  "name": "BGBiao",
                  "website": "https://bgbiao.top"
              },
              "message": "",
              "status": true
          }%
          

          當然上面我們僅以JSON格式來示例,類似的方式我們可以使用XML,YAML,ProtoBuf等方法來輸出指定格式化后的數據。

          3.2其他常用的基本方法

          注意:在其他基本方法中我們仍然使用上述示例代碼中的主邏輯,主要用來測試基本的方法.

          # 我們在/api/test這個路由中增加如下兩行代碼
          // 設置響應體中的自定義header(通常我們可以通過自定義頭來實現一個內部標識)
          c.Header("Api-Author","BGBiao")
          // GetHeader方法用來獲取指定的請求頭,比如我們經常會使用請求中的token來進行接口的認證和鑒權
          // 這里由于我們使用的restdata的指針,通過GetHeader方法獲取到token賦值給Message
          // ClientIP()方法用于獲取客戶端的ip地址
          restdata.Message=fmt.Sprintf("token:%s 當前有效,客戶端ip:%s",c.GetHeader("token"),c.ClientIP())
          
          # 訪問接口示例(我們可以看到在響應體中多了一個我們自定義的Api-Author頭,并且我們將請求頭token的值)
          $ curl -H 'token:xxxxxxxx' localhost:8080/api/test -i
          HTTP/1.1 200 OK
          Api-Author: BGBiao
          Content-Type: application/json; charset=utf-8
          Date: Sun, 12 Jan 2020 14:41:01 GMT
          Content-Length: 66
          
          {"data":"Hello,BGBiao","message":"token:xxxxxxxx 當前有效,客戶端ip:127.0.0.1","status":true}
          

          3.3用戶數據輸入

          當然到這里后,你可能還會有新的疑問,就是通常情況下,我們開發后端接口會提供一些具體的參數,通過一些具體數據提交來實現具體的業務邏輯處理,這些參數通常會分為如下三類:

          • 使用HTTP GET方法獲取到的url中的一些查詢參數來執行更具體的業務邏輯(比如我們查詢數據的指定條數之類的)
          • 使用HTTP POST GET等其他方式以form表單方式提交的數據來驗證和處理用戶數據
          • 在URL中獲取一些可變參數(比如通常我們的url會定義為"/api/uid/:id"來表示用戶id相關的接口,這個時候通常需要獲取到url中的id字段)

          以上的基本需求,幾乎都可以在Context結構體的輸入數據中找到響應的方法.


          主站蜘蛛池模板: 中文字幕在线一区| 无码人妻AⅤ一区二区三区水密桃| 久久99精品一区二区三区| 日韩精品人妻一区二区三区四区| 亚洲熟妇av一区二区三区| 制服美女视频一区| 97久久精品无码一区二区| 国产免费一区二区三区在线观看| 国产在线精品一区在线观看| 无码中文人妻在线一区二区三区| 蜜臀Av午夜一区二区三区| 亚洲乱码日产一区三区| 国模无码人体一区二区| 一区在线免费观看| 一级毛片完整版免费播放一区 | 国产高清在线精品一区| 亚洲AV无码一区东京热| 久久国产精品亚洲一区二区| 免费观看一区二区三区| 亚洲一区二区女搞男| 国产亚洲3p无码一区二区| 美女啪啪一区二区三区| 久久久久一区二区三区| 天堂资源中文最新版在线一区 | 日韩高清国产一区在线| 久久久久久人妻一区精品| 无码人妻精品一区二区三区久久 | 加勒比精品久久一区二区三区| 无码日本电影一区二区网站| 亚洲色精品三区二区一区| 色国产在线视频一区| 精品一区二区三区在线视频观看 | 寂寞一区在线观看| 中文字幕视频一区| 色欲AV蜜桃一区二区三| 动漫精品第一区二区三区| 亚洲av成人一区二区三区在线观看 | 久久精品无码一区二区无码 | 亲子乱av一区二区三区| 日韩人妻不卡一区二区三区 | 国精产品一区一区三区有限公司 |