Golang Gin 實戰(八)| JSON渲染輸出

  • 2020 年 1 月 23 日
  • 筆記

現在開發API,所輸出的基本上都是JSON格式的內容,相比比較舊的XML格式,JSON輕便、簡潔、易於傳輸,所以現在的API使用非常多。

Gin對於API JSON的支援非常友好,可以讓我們非常方便的開發一個基於JSON的API。

快速入門

1 2 3 4 5 6 7

func main() { r := gin.Default() r.GET("/hello", func(c *gin.Context) { c.JSON(200, gin.H{"message": "hello world"}) }) r.Run(":8080") }

一個非常簡單的例子,主要知識點在於c.JSON方法,它可以讓我們非常方便的輸出JSON格式的內容。

現在運行打開瀏覽器訪問http://localhost:8080/hello可以看到如下內容:

{"message":"hello world"}

這是一個JSON格式的字元串,第三方調用者可以獲得這個JSON內容,把它轉換為一個JSON對象,然後通過message欄位獲取對應的值,也就是hello world

這裡我們使用了gin.H這個類型來構建了一個鍵值對對象,其實gin.H是一個map[string]interface{}

1 2

// H is a shortcut for map[string]interface{} type H map[string]interface{}

gin.H主要是為了幫助我們開發者很方便的構建出一個map對象,不止用於c.JSON方法,也可以用於其他場景。

Struct 轉 JSON

c.JSON方法非常強大,不止可以用於map的輸出,還可以把我們自定義的對象struct轉為一個json字元串輸出。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

func main() { r := gin.Default() r.GET("/users/123", func(c *gin.Context) { c.JSON(200, user{ID: 123, Name: "張三", Age: 20}) }) r.Run(":8080") } type user struct { ID int Name string Age int }

這個例子中我們自定義了一個user struct 來表示用戶,然後我們註冊一個用戶ID為123的路由,用於輸出這個用戶的資訊,這裡使用的就是user struct,把它作為參數直接傳給c.JSON方法即可。

現在我們運行,在瀏覽器里訪問http://localhost:8080/users/123 可以看到如下資訊:

{"ID":123,"Name":"張三","Age":20}

完美、簡單的把這個用戶的資訊作為一個JSON字元串輸出。

自定義JSON欄位名稱

看上面的例子,我們發現輸出的JSON字元串的欄位和我們定義的user的欄位名一樣,但是這樣的命名格式顯然不太適合JSON,因為JSON的欄位應該是小寫字母開頭的,這比較符合當前大家所遵守的JSON風格。

Gin是支援欄位名字重新命名的,並且很簡單,和Golang原生的JSON一樣。

1 2 3 4 5

type user struct { ID int `json:"id"` Name string `json:"name"` Age int `json:"age"` }

只需要在user struct 定義的時候為欄位添加json tag即可。關於 Struct Tag 的內容請參考我以前寫的 Go語言實戰筆記(二十五)| Go Struct Tag 這篇文章,這裡不再贅述。

現在我們重新運行,在瀏覽器里訪問http://localhost:8080/users/123 ,發現看到的資訊已經變了:

{"id":123,"name":"張三","age":20}

更符合JSON的風格了,看著更順眼一些。

JSON數組

在一些情況下,比如我們需要獲取所有用戶資訊,那麼表達為JSON字元串來說,就是一個JSON數組。在Gin中,生成JSON數組也很簡單,只要我們傳遞給c.JSON的參數是個數組就可以。

1 2 3 4

allUsers := []user{{ID: 123, Name: "張三", Age: 20}, {ID: 456, Name: "李四", Age: 25}} r.GET("/users", func(c *gin.Context) { c.IndentedJSON(200, allUsers) })

我們首先定義了一個user數組,然後使用c.IndentedJSON輸出JSON字元串,現在運行打開瀏覽器,訪問http://localhost:8080/users就可以看到如下資訊,一個JSON數組字元串:

[      {          "id": 123,          "name": "張三",          "age": 20      },      {          "id": 456,          "name": "李四",          "age": 25      }  ]

IndentedJSON 美化

上面的例子,我們可以看到,輸出的JSON字元串都是扁平的,沒有縮進,不美觀。對於這種情況,Gin也為我們提供了便捷的方法,讓我們輸出的JSON更好看。

1 2 3

r.GET("/users/456", func(c *gin.Context) { c.IndentedJSON(200, user{ID: 456, Name: "李四", Age: 25}) })

想美化JSON的輸出,使用c.IndentedJSON方法即可,現在現在我們運行訪問http://localhost:8080/users/456就可以看到JSON已經被美化了,更容易看了。。

{      "id": 456,      "name": "李四",      "age": 25  }

PureJSON

對於JSON字元串中特殊的字元串,比如<,Gin默認是轉義的,比如變成 u003c,但是有時候我們為了可讀性,需要保持原來的字元,不進行轉義,這時候我們就可以使用PureJSON

1 2 3 4 5 6 7 8 9 10 11 12

r.GET("/json", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "<b>Hello, world!</b>", }) }) r.GET("/pureJson", func(c *gin.Context) { c.PureJSON(200, gin.H{ "message": "<b>Hello, world!</b>", }) })

用這兩個API進行對比,我們運行訪問http://localhost:8080/json,顯示資訊如下:

{"message":"u003cbu003eHello, world!u003c/bu003e"}

特殊字元已經被轉義了,現在訪問http://localhost:8080/pureJson,顯示的就是原始資訊:

{"message":"<b>Hello, world!</b>"}

可讀性更強。

AsciiJSON

如果要把非Ascii字元串轉為unicode編碼,Gin同樣提供了非常方便的方法。

1 2 3

r.GET("/asciiJSON", func(c *gin.Context) { c.AsciiJSON(200, gin.H{"message": "hello 飛雪無情"}) })

通過c.AsciiJSON方法,Gin可以把所有的非Ascii字元全部轉義為unicode編碼,現在我們運行看看結果。

{"message":"hello u98deu96eau65e0u60c5"}

飛雪無情4個字已經被轉義為u98deu96eau65e0u60c5了,避免亂碼。

加速JSON

在Gin中,提供了兩種JSON解析器,用於生成JSON字元串。默認的是Golang(Go語言)內置的JSON,當然你也可以使用jsoniter,據說速度很快。如果要使用jsoniter,我們在go build編譯的時候只需要這麼做即可:

go build -tags=jsoniter .

這樣我們就是用了jsoniter,是基於條件編譯的技術,具體可以參考我這篇文章 Go語言中自動選擇json解析庫

精彩文章推薦

Golang Gin 實戰(七)| 分組路由源程式碼分析

Golang Gin 實戰(六)| 獲取Form表單參數和原理分析

Golang Gin 實戰(五)| 接收數組和map

Golang Gin 實戰(四)| URL查詢參數的獲取和原理分析

Golang Gin 實戰(三)| 路由參數

Golang Gin 實戰(二)| 簡便的Restful API 實現

Golang Gin 實戰(一)| 快速安裝入門