golang之Gin框架學習


Gin框架介紹及環境搭建

Gin特點和特性

  速度:之所以被很多企業和團隊所采用,第一個原因是因為其速度快,性能表現初衷;

  中間件:和iris類似,Gin在處理請求時,支持中間件操作,方便編碼處理;

  路由:在Gin中可以非常簡單的實現路由解析的功能,並包含路由組解析功能;

  內置渲染:Gin支持JSON、XML和HTML等多種數據格式的渲染,並提供了方便的操作API。

安裝Gin框架

go get -u github.com/gin-gonic/gin

Hello World:

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "log"
)

func main() {
    engine := gin.Default()

    engine.GET("/hello", func(context *gin.Context) {
        fmt.Println(" 請求路徑: ", context.FullPath())
        context.Writer.Write([]byte("hello world!\n"))
    })

    if err := engine.Run(":8090"); err != nil {
        log.Fatal(err.Error())
    }
}

Gin網絡請求與路由處理

engine1 = gin.Default()
engine2 = gin.New()

gin.Default()也使用gin.New()創建engine實例,但是會默認使用Logger和Recovery中間件。

Logger負責進行打印並輸出日志的中間件,方便開發者進行程序調試;

Recovery中間件的作用是如果程序執行過程中遇到panic中斷了服務,則Recovery會恢復程序執行,並返回服務器500內部錯誤。

處理HTTP請求

通用處理:

func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes
httpMethod:第一個參數表示要處理的HTTP的請求類型,是GET/POST/DELETE等請求類型中的一種。
relativePath:第二個參數表示要解析的接口,由開發想着進行定義。
handlers:第三個參數是處理對應的請求的代碼的定義。

如:
engine.Handle("GET", "/hello", func(context *gin.Context) {
        fmt.Println(context.FullPath())
        userName := context.Query("name")
        fmt.Println(userName)
        context.Writer.Write([]byte("hello: " + userName))
    })

Context是gin框架中封裝的一個結構體,這是gin框架中最重要,最基礎的一個結構體對象。該結構體可以提供我們操作請求,處理請求,獲取數據等相關的操作,通常稱之為上下文對象,簡單說為我們提供操作環境。

可以通過context.Query和context.DefaultQuery獲取GET請求攜帶的參數。

 

分類處理:

engine.GET("/hello", func(context *gin.Context) {
username := context.Query("name")
context.Writer.Write([]byte("hello world!" + username))
})

context.DefaultQuery: 除了context.DefaultQuery方法獲取請求攜帶的參數數據以外,還可以使用context.Query方法來獲取Get請求攜帶的參數。

engine.POST("/login", func(context *gin.Context) {
username, exist := context.GetPostForm("username")
if exist {
fmt.Println(username)
}
context.Writer.Write([]byte("hello world!" + username))
})

context.GetPostForm獲取表單數據:POST請求以表單的形式提交數據,除了可以使用context.PostForm獲取表單數據意外,還可以使用context.GetPostForm來獲取表單數據。

engine.DELETE("/user/:id", DeleteHandle)
func DeleteHandle(context *gin.Context) {
    userID := context.Param("id")
    context.Writer.Write([]byte("Delete user's id : " + userID))
}

客戶端的請求接口是DELETE類型,請求url為:http://localhost:8080/user/1。最后的1是要刪除的用戶的id,是一個變量。因此在服務端gin中,通過路由的:id來定義一個要刪除用戶的id變量值,同時使用context.Param進行獲取。

請求參數綁定與多數據格式處理

Gin框架提供給開發者表單實體綁定的功能,可以將表單數據與結構體綁定。

表單實體綁定

使用PostForm這種單個獲取屬性和字段的方式,代碼量較多,需要一個一個屬性進行獲取。而表單數據的提交,往往對應着完整的數據結構體定義,其中對應着表單的輸入項。gin框架提供了數據結構體和表單提交數據綁定的功能,提高表單數據獲取的效率。

以一個用戶注冊功能來進行講解表單實體綁定操作。用戶注冊需要提交表單數據,假設注冊時表單數據包含三項,分別為:username、phone和password。

type UserRegister struct {
    Username string form:"username" binding:"required"
    Phone    string form:"phone" binding:"required"
    Password string form:"password" binding:"required"
}

ShouldBindQuery

使用ShouldBindQuery可以實現Get方式的數據請求的綁定

func main() {
    engine := gin.Default()

    // http://localhost:8080/hello?name=davie&classes=軟件工程
    engine.GET("/hello", func(context *gin.Context) {
        fmt.Println(context.FullPath())
        user := User{}
        if err := context.ShouldBindQuery(&user); err != nil {
            fmt.Println("信息有誤!")
        }
        fmt.Println(user.UserName)
        fmt.Println(user.Classes)
        context.Writer.Write([]byte("username: " + user.UserName + " classes: " + user.Classes))
    })

    engine.Run()
}

type User struct {
    UserName string `form:"name"`
    Classes  string `form:"classes"`
}

ShouldBind

使用ShouldBind可以實現Post方式的提交數據的綁定工作

func main() {
    engine := gin.Default()

    engine.POST("register", func(context *gin.Context) {
        fmt.Println(context.FullPath())
        var register Register
        if err := context.ShouldBind(&register); err != nil {
            fmt.Println("注冊失敗!")
        }
        fmt.Println(register.UserName)
        fmt.Println(register.Phone)
        fmt.Println(register.Password)
        context.Writer.Write([]byte("register success! " + register.UserName))
    })

    engine.Run()
}

type Register struct {
    UserName string `form:"name"`
    Phone    string `form:"phone"`
    Password string `form:"password"`
}

ShouldBindJson

當客戶端使用Json格式進行數據提交時,可以采用ShouldBindJson對數據進行綁定並自動解析

func main() {
    engine := gin.Default()

    engine.POST("/addstudent", func(context *gin.Context) {
        fmt.Println(context.FullPath())
        var person Person
        if err := context.ShouldBindJSON(&person); err !=nil {
        //if err := context.BindJSON(&person); err != nil {
            fmt.Println("失敗!")
        }
        fmt.Println(person.Name)
        fmt.Println(person.Sex)
        fmt.Println(person.Age)
        //context.Writer.Write([]byte(" 添加記錄:" + person.Name))
        context.Writer.WriteString(context.FullPath())
    })

    engine.Run()
}

type Person struct {
    Name string `form:"name"`
    Sex  string `form:"sex"`
    Age  int    `form:"age"`
}

gin還支持其他多種方式

多數據格式返回請求結果

在gin框架中,支持返回多種請求數據格式

[]byte

engine := gin.Default()
engine.GET("/hello", func(context *gin.Context) {
        fullPath := "請求路徑:" + context.FullPath()
        fmt.Println(fullPath)
        context.Writer.Write([]byte(fullPath))
})
engine.Run()

使用context.Writer.Write向客戶端寫入返回數據。Writer是gin框架中封裝的一個ResponseWriter接口類型,其中的write方法就是http.ResponseWriter中包含的方法。

type ResponseWriter interface {
    http.ResponseWriter
    http.Hijacker
    http.Flusher
    http.CloseNotifier

    // Returns the HTTP response status code of the current request.
    Status() int

    // Returns the number of bytes already written into the response http body.
    // See Written()
    Size() int

    // Writes the string into the response body.
    WriteString(string) (int, error)

    // Returns true if the response body was already written.
    Written() bool

    // Forces to write the http header (status code + headers).
    WriteHeaderNow()

    // get the http.Pusher for server push
    Pusher() http.Pusher
}

string

除了write方法以外,ResponseWriter自身還封裝了WriteString方法返回數據

// Writes the string into the response body.
WriteString(string) (int, error)

和[]byte類型調用一樣,可以通過Writer進行調用

engine.GET("/hello", func(context *gin.Context) {
        fullPath := "請求路徑:" + context.FullPath()
        fmt.Println(fullPath)
        context.Writer.WriteString(fullPath)
})

JSON

map類型

engine := gin.Default()
engine.GET("/hellojson", func(context *gin.Context) {
    fullPath := "請求路徑:" + context.FullPath()
    fmt.Println(fullPath)

    context.JSON(200, map[string]interface{}{
        "code":    1,
        "message": "OK",
        "data":    fullPath,
    })
})
engine.Run(":9000") 

調用JSON將map類型的數據轉換成為json格式並返回給前端,第一個參數200表示設置請求返回的狀態碼。和http請求的狀態碼一致。

結構體類型

除了map以外,結構體也是可以直接轉換為JSON格式進行返回的

//通用請求返回結構體定義
type Response struct {
    Code    int         json:"code"
    Message string      json:"msg"
    Data    interface{} json:"data"
}

engine.GET("/jsonstruct", func(context *gin.Context) {
    fullPath := "請求路徑:" + context.FullPath()
    fmt.Println(fullPath)
    resp := Response{Code: 1, Message: "Ok", Data: fullPath}
    context.JSON(200, &resp)
})

HTML模板

除了JSON格式以外,gin框架還支持返回HTML格式的數據

engine := gin.Default()
//設置html的目錄
engine.LoadHTMLGlob("./html/*")
engine.GET("/hellohtml", func(context *gin.Context) {
    fullPath := "請求路徑:" + context.FullPath()

    context.HTML(http.StatusOK, "index.html", gin.H{
        "title":    "Gin框架",
        "fullpath": fullPath,
    })
})
engine.Run()

加載靜態資源文件

如果需要再頁面是添加一張img,需要將img所在的目錄進行靜態資源路徑設置才可能會生效:

engine.Static("/img", "./img")

同理,在項目開發時,一些靜態的資源文件如html、js、css等可以通過靜態資源文件設置的方式來進行設置

 

使用路由組分類處理請求

在實際的項目開發中,均是模塊化開發。同一模塊內的功能接口,往往會有相同的接口前綴

注冊:http://localhost:9000/user/register
登錄:http://localhost:9000/user/login
用戶信息:http://localhost:9000/user/info
刪除:http://localhost:9000/user/1001

類似這種接口前綴統一,均屬於相同模塊的功能接口。可以使用路由組進行分類處理。

Group

gin框架中可以使用路由組來實現對路由的分類。

路由組是router.Group中的一個方法,用於對請求進行分組。

    engine := gin.Default()

    userGroup := engine.Group("/user")
    userGroup.POST("/register", registerHandle)
    userGroup.POST("/login", loginHandle)
    userGroup.GET("/info", infoHandle)

    engine.Run()

Group返回一個RouterGroup指針對象,而RouterGroup是gin框架中的一個路由組結構體定義。

type RouterGroup struct {
    Handlers HandlersChain
    basePath string
    engine   *Engine
    root     bool
}

RouterGroup實現了IRoutes中定義的方法,包含統一處理請求的Handle和分類型處理的GET、POST等。

type IRoutes interface {
    Use(...HandlerFunc) IRoutes

    Handle(string, string, ...HandlerFunc) IRoutes
    Any(string, ...HandlerFunc) IRoutes
    GET(string, ...HandlerFunc) IRoutes
    POST(string, ...HandlerFunc) IRoutes
    DELETE(string, ...HandlerFunc) IRoutes
    PATCH(string, ...HandlerFunc) IRoutes
    PUT(string, ...HandlerFunc) IRoutes
    OPTIONS(string, ...HandlerFunc) IRoutes
    HEAD(string, ...HandlerFunc) IRoutes

    StaticFile(string, string) IRoutes
    Static(string, string) IRoutes
    StaticFS(string, http.FileSystem) IRoutes
}

middleware的編寫與使用

中間件

在web應用服務中,完整的一個業務處理在技術上包含客戶端操作、服務器端處理、返回處理結果給客戶端三個步驟。

在實際的業務開發和處理中,會有更負責的業務和需求場景。一個完整的系統可能要包含鑒權認證、權限管理、安全檢查、日志記錄等多維度的系統支持。

鑒權認證、權限管理、安全檢查、日志記錄等這些保障和支持系統業務屬於全系統的業務,和具體的系統業務沒有關聯,對於系統中的所有業務都適用。

由此,在業務開發過程中,為了更好的梳理系統架構,可以將上述描述所涉及的一些通用業務單獨抽離並進行開發,然后以插件化的形式進行對接。這種方式既保證了系統功能的完整,同時又有效的將具體業務和系統功能進行解耦,並且,還可以達到靈活配置的目的。

這種通用業務獨立開發並靈活配置使用的組件,一般稱之為"中間件",因為其位於服務器和實際業務處理程序之間。其含義就是相當於在請求和具體的業務邏輯處理之間增加某些操作,這種以額外添加的方式不會影響編碼效率,也不會侵入到框架中。

 

Gin的中間件

在gin中,中間件稱之為middleware

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

HandlerFunc是一個函數類型,接收一個Context參數。用於編寫程序處理函數並返回HandleFunc類型,作為中間件的定義。

中間件Use用法

使用gin.Default創建了gin引擎engins變量,其中,就使用了中間件。

func Default() *Engine {
    debugPrintWARNINGDefault()
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}
//Log中間件
func Logger() HandlerFunc {
    return LoggerWithConfig(LoggerConfig{})
}
//Recovery中間件
func Recovery() HandlerFunc {
    return RecoveryWithWriter(DefaultErrorWriter)
}

在Default函數中,engine調用Use方法設置了Logger中間件和Recovery中間件。Use函數接收一個可變參數,類型為HandlerFunc,恰為中間件的類型。

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    engine.RouterGroup.Use(middleware...)
    engine.rebuild404Handlers()
    engine.rebuild405Handlers()
    return engine
}

自定義中間件

根據上文的介紹,可以自己定義實現一個特殊需求的中間件,中間件的類型是函數,有兩條標准:

  func函數

  返回值類型為HandlerFunc

func main() {
    engine := gin.Default()

    //engine.Use(RequestInfos())
    engine.GET("/hello", RequestInfos(), func(context *gin.Context) {
        fmt.Println("中間件的使用方法")
        context.JSON(200, map[string]interface{}{
            "code": 1,
            "msg":  "very good!",
        })
    })

    engine.POST("/register", func(context *gin.Context) {
        context.JSON(200, map[string]interface{}{
            "code": 233,
            "msg": "you is dog!",
        })
    })

    engine.Run()
}

func RequestInfos() gin.HandlerFunc {
    return func(context *gin.Context) {
        path := context.FullPath()
        method := context.Request.Method
        fmt.Println("請求url:", path)
        fmt.Println("請求method:", method)
        context.Next()
        fmt.Println("狀態碼:", context.Writer.Status())
    }
}

context.Next函數

 在上文自定義的中間件RequestInfos中,打印了請求了請求的path和method,接着去執行了正常的業務處理函數。如果我們想輸出業務處理結果的信息,該如何實現呢。答案是使用context.Next函數。

context.Next函數可以將中間件代碼的執行順序一分為二,Next函數調用之前的代碼在請求處理之前之前,當程序執行到context.Next時,會中斷向下執行,轉而先去執行具體的業務邏輯,執行完業務邏輯處理函數之后,程序會再次回到context.Next處,繼續執行中間件后續的代碼。

Next函數的作用及代碼執行流程示意圖如下圖所示:

  • 1、程序先執行①和②。
  • 2、執行到③時,轉而去執行業務處理程序。
  • 3、返回到中間件中,執行④。

 

Gin框架中使用數據庫

Gin鏈接和使用MySQL數據庫

1、安裝MySQL驅動

go get "github.com/go-sql-driver/mysql"

2、創建數據庫

mysql -uroot -p
create database ginsql;

3、在gin中使用編程語言進行連接數據庫

在gin中連接和操作mysql與在其他框架中連接操作mysql沒有什么區別

  a、引入mysql驅動程序:使用import將mysql驅動默認引入

import _ "github.com/go-sql-driver/mysql"

  b、拼接鏈接字符:在程序中鏈接mysql,需要按照一定的規則進行用戶名,密碼等信息的組合

connStr := "root:password@tcp(127.0.0.1:3306)/ginsql"

  c、使用sql.Open創建數據庫連接

db, err := sql.Open("mysql", connStr)
    if err != nil {
        log.Fatal(err.Error())
        return
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM