Go-Gin frame

1. Introduction to Gin

Gin is an HTTP web framework written in Go . It is an API framework similar to martini but with better performance, better than httprouter and nearly 40 times faster. Click here to access Gin official Chinese documentation.

2. Installation

1. Install Gin

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

2. Introduced into the code

import "github.com/gin-gonic/gin"

3. Simple example, verification

Create a new file main.go with the following content:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    // 1.创建路由
   r := gin.Default()
   // 2.绑定路由规则,执行的函数
   // gin.Context,封装了request和response
   r.GET("/", func(c *gin.Context) {
      c.String(http.StatusOK, "hello World!")
   })
   // 3.监听端口,默认在8080
   // Run("")里面不指定端口号默认为8080
   r.Run(":8000")
}

After running, visit:  http://localhost:8000/

3. Cross-domain rendering front-end and configuration


1. Render html template


Gin supports loading HTML templates, then configuring them according to template parameters and returning corresponding data, which is essentially string replacement. The LoadHTMLGlob() method can load the template file (the parameter is the html template file to be rendered, if the parameter is a relative path, it is the relative path of the running path).

a. Render a single file

r.LoadHTMLGlob("web/index.html")


b. Render all files in the folder

r.LoadHTMLGlob("web/*")


c. Render all files with html suffix in the folder.
For example:

r.LoadHTMLGlob("web/*.html")



2. Define template segmentation

r.Delims("<<<", ">>>")

The first parameter is: template tag start tag.
The second parameter is: template tag end tag.

3. Render static files and directories


If you need to introduce static files, you need to define a static file directory

r.Static("/assets", "./static/assets")


If the directory structure of assets is


The specified
resource

 

 If r is a routing group, the path of the routing group needs to be spliced ​​before assets including its prefix.

4. Redirect

// 重定向两种默认应支持的首页访问方式
router.GET("/", func(c *gin.Context) {
	//重定向到/index.html
	c.Redirect(302, "/index.html")
})
router.GET("/index", func(c *gin.Context) {
	//重定向到/index.html
	c.Redirect(302, "/index.html")
})
router.GET("/index.html", func(c *gin.Context) {
	//返回渲染的html模板中的index.html
	c.HTML(http.StatusOK, "index.html", gin.H{
		"baseUrl": "http://" + host,
	})
})

5. Configure cross-domain

Next()

r.Use(Next())

Allow cross domain

// 允许跨域
func Next() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers,Authorization,User-Agent, Keep-Alive, Content-Type, X-Requested-With,X-CSRF-Token,AccessToken,Token")
		c.Header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, OPTIONS")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		c.Header("Access-Control-Allow-Credentials", "true")

		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusAccepted)
		}
		c.Next()
	}
}

6. Response format c.String() c.JSON() c.JSONP() c.XML() c.HTML()

(1) Return a string

r.GET("/news", func(c *gin.Context) {
    aid := c.Query("aid")
    c.String(200, "aid=%s", aid)
})


(2) Return a JSON data
   

//方法一:自己拼接JSON
    r.GET("/json", func(c *gin.Context) {
        //返回json数据,使用 map[string]interface
        //c.JSON(返回的状态码, 任意类型的数据(如:map,struct,...)
        c.JSON(200, map[string]interface{}{
            "success": true,
            "msg":     "你好",
        })
    })
    //方法二:gin中的H函数
    r.GET("/json2", func(c *gin.Context) {
        //返回json数据,使用gin中的H函数, gin.H 是 map[string]interface{}的缩写
        c.JSON(200, gin.H{
            "success": true,
            "msg":     "你好gin",
        })
    })
    //方法三:使用结构体
    r.GET("/json3", func(c *gin.Context) {
        //实例化一个结构体
        a := &Article{
            Title:   "标题",
            Desc:    "说明",
            Content: "内容",
        }
        c.JSON(200, a)
    })

JSOPN: jsonp requests are mainly used to solve cross-domain problems
 

  //jsonp请求 主要用来解决跨域问题
    //http://127.0.0.1:8080/jsonp?callback=call
    //call({"title":"标题-jsonp","desc":"说明-jsonp","content":"内容-jsonp"});
    r.GET("/jsonp", func(c *gin.Context) {
        //实例化一个结构体
        a := &Article{
            Title:   "标题-jsonp",
            Desc:    "说明-jsonp",
            Content: "内容-jsonp",
        }
        c.JSONP(200, a)
    })


(3) Return XML data
   

//方法一:使用gin.H返回
    r.GET("/xml", func(c *gin.Context) {
        c.XML(http.StatusOK, gin.H{
            "success": true,
            "msg":     "成功xml",
        })
    })
    //方法二:使用结构体
    r.GET("/xmlStruct", func(c *gin.Context) {
        //实例化一个结构体
        a := &Article{
            Title:   "标题-xmlStruct",
            Desc:    "说明-xmlStruct",
            Content: "内容-xmlStruct",
        }
        c.XML(200, a)
    })

(4) Return HTML data

     //初始化路由
     r := gin.Default()
     //加载templates文件中所有模板文件,以便后续c.HTML()渲染文件时使用
     r.LoadHTMLGlob("templates/*")
     r.GET("/news", func(c *gin.Context) {
        //使用模板文件渲染HTML文件
        //前提: r.LoadHTMLGlob("templates/*")
        //HTML(状态码, 要渲染的文件名, 加载的参数)
        c.HTML(http.StatusOK, "news.html", gin.H{
            "title": "我是一个news",
        })
    })


The complete code example is as follows:

package main
 
import (
    "github.com/gin-gonic/gin"
    "net/http"
)
 
type Article struct {
    Title   string `json:"title"`
    Desc    string `json:"desc"`
    Content string `json:"content"`
}
 
func main() {
    //初始化路由
    r := gin.Default()
    //加载templates文件中所有模板文件,以便后续c.HTML()渲染文件时使用
    r.LoadHTMLGlob("templates/*")
    //配置路由
    r.GET("/", func(c *gin.Context) {
        c.String(200, "首页")
    })
    r.GET("/json", func(c *gin.Context) {
        //返回json数据,使用 map[string]interface
        //c.JSON(返回的状态码, 任意类型的数据(如:map,struct,...)
        c.JSON(200, map[string]interface{}{
            "success": true,
            "msg":     "你好",
        })
    })
    r.GET("/json2", func(c *gin.Context) {
        //返回json数据,使用gin中的H函数
        c.JSON(200, gin.H{
            "success": true,
            "msg":     "你好gin",
        })
    })
    r.GET("/json3", func(c *gin.Context) {
        //实例化一个结构体
        a := &Article{
            Title:   "标题",
            Desc:    "说明",
            Content: "内容",
        }
        c.JSON(200, a)
    })
    //jsonp请求 主要用来解决跨域问题
    //http://127.0.0.1:8080/jsonp?callback=call
    //call({"title":"标题-jsonp","desc":"说明-jsonp","content":"内容-jsonp"});
    r.GET("/jsonp", func(c *gin.Context) {
        //实例化一个结构体
        a := &Article{
            Title:   "标题-jsonp",
            Desc:    "说明-jsonp",
            Content: "内容-jsonp",
        }
        c.JSONP(200, a)
    })
    r.GET("/xml", func(c *gin.Context) {
        c.XML(http.StatusOK, gin.H{
            "success": true,
            "msg":     "成功xml",
        })
    })
    r.GET("/news", func(c *gin.Context) {
        //使用模板文件渲染HTML文件
        //前提: r.LoadHTMLGlob("templates/*")
        //HTML(状态码, 要渲染的文件名, 加载的参数)
        c.HTML(http.StatusOK, "news.html", gin.H{
            "title": "我是一个news",
        })
    })
    r.GET("/goods", func(c *gin.Context) {
        //使用模板文件渲染HTML文件
        //前提: r.LoadHTMLGlob("templates/*")
        //HTML(状态码, 要渲染的文件名, 加载的参数)
        c.HTML(http.StatusOK, "goods.html", gin.H{
            "title": "我是一个goods",
            "price": 12.99,
        })
    })
    r.Run() // 启动一个web服务
}

4. Routing related

1. Create a routing group

// 创建路由组
// 根据需要,可以为这种多级的路由组:r.Group("/v1/user")
userApi:= r.Group("/user")

// 创建用户
// 匹配POST请求的 /user
userApi.POST("", userCreate)

// 修改用户
// 匹配PUT请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.PUT("/:id", userUpdate)

// 获取用户
// 匹配GET请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.GET("/:id", userGet)

// 查询用户
// 匹配GET请求的 /user/list
userApi.GET("/list", userQuery)

// 删除用户
// 匹配DELETE请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.DELETE("/:id", userDelete)

Restful style API

2. Get routing parameters

a. api parameters

Get api parameters through Context's Param method
 

userApi.GET("/:id/:name", userGet)

The first parameter: Get the value of url path parameters id and name

Second parameter: userGet function

func userGet(ctx *gin.Context) {
	//api参数可以为单个或者多个也可以拼接多级

	//ctx.Param()函数获取时参数需要与api中的名称一致才能获取到

	id := ctx.Param("id")
	name:= ctx.Param("name")

	ctx.JSON(http.StatusOK, gin.H{"data": id,"name":name})

	return
}

http://localhost:8000/user/1/admin

b. url parameters

Get url parameters through Context's Query method and DefaultQuery method

userApi.GET("/list", userQuery)

userQuery method

func userQuery(ctx *gin.Context) {
	//获取
	id := ctx.Query("id")
	//获取,第二个参数为获取为空的默认值,如果参数不存在则放回第二个参数
	name := ctx.DefaultQuery("name", "user")
	ctx.JSON(http.StatusOK, gin.H{"data": id, "name": name})
	return
}

Note: The default value is when the client does not bring this parameter in the request, the server will take the name and the default value is "user"

You can access this interface by splicing query parameters behind http://localhost:8000/user/list.

 If the client does not pass parameters, the default value will be used.

 If the client has passed parameters, the default value will not be taken.

c. Form parameters


To test form parameters and observe request effects, you need to install postman
to obtain form parameters through the PostForm method of Context.

userApi.POST("", userCreate)

The userCreate function is

func userCreate(ctx *gin.Context) {
    id := ctx.PostForm("id")
    name := ctx.PostForm("name")
    ctx.JSON(http.StatusOK, gin.H{"data": id, "name": name})
    return
}



As shown in the figure, access this interface

d. json parameters

To test json parameters and observe request effects, you need to install postman to obtain form parameters through the GetRawData
or ShouldBindJSON method of Context.

userApi.PUT("/:id", userUpdate)

userUpdate function is


GetRawData方法

func userUpdate(ctx *gin.Context) {
	
	b, err := ctx.GetRawData() // 从ctx.Request.Body读取请求数据
    if err !=nil{
        fmt.print(err)
    }
	// 定义map或结构体
	var m map[string]interface{}
	// 反序列化
	_ = json.Unmarshal(b, &m)
	ctx.JSON(http.StatusOK, gin.H{"data": m["id"], "name": m["name"]})
	return
}

ShouldBindJSON method

// 先定义结构
type User struct {
	Id   string `form:"id" json:"id" binding:"required"`
	Name string `form:"name" json:"name" binding:"required"`
}

// 函数实现
func userUpdate(ctx *gin.Context) {
	var user User
	if err := ctx.ShouldBindJSON(&user); err == nil {
		ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})
		return
	} else {
		ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
		return
	}
}

e. Parameter binding

In order to obtain request-related parameters more conveniently and improve development efficiency, we can identify the request data type based on the Content-Type of the request and use the reflection mechanism to automatically extract form, JSON, XML and other parameters in the request into the structure.


Code example:


Define the structure to be obtained

type User struct {
	Id   string `form:"id" json:"id" binding:"required"`
	Name string `form:"name" json:"name" binding:"required"`
}

userCreate function

func userCreate(ctx *gin.Context) {
    // 实例化结构体对象
	var user User

	if err := ctx.ShouldBind(&user); err == nil {
        // 响应体 json 格式
		ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})
		return
	}else{
		ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
		return
	}
}

userUpdate function

func userUpdate(ctx *gin.Context) {
    // 实例化结构体对象
	var user User
	if err := ctx.ShouldBind(&user); err == nil {
		ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})
		return
	}else{
		ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
		return
	}
}

3. Upload files

Generally, parameters are passed in the post request form.

userApi.POST("/upload", userUpload)

userUpload function

// 先定义结构
type FileUpload struct {
	File *multipart.FileHeader `form:"file"`
	Type string                `form:"type"`
}
// 函数实现
func userUpload(ctx *gin.Context) {
	var fileUpload FileUpload
// 
	if err := ctx.ShouldBind(&fileUpload); err == nil {
		//获取运行路径
		ex, err := os.Executable()
        //
		if err != nil {
			ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
			return
		}
		//定义接收文件的存放地址
		path := filepath.Dir(ex) + string(os.PathSeparator) + fileUpload.File.Filename
		//接收文件并保存到指定path
		err = ctx.SaveUploadedFile(fileUpload.File, path)
		ctx.JSON(http.StatusOK, gin.H{"data": path, "type": fileUpload.Type})
		return
	} else {
		ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
		return
	}
}

5. Routing group file

When using the Gin framework to write Go projects, if all routes are written in a .go file, as the number of routes increases, the routing file will become extremely bloated.

At this time, routes need to be grouped and managed in files, which is more conducive to code organization and maintenance.

When using the Gin framework to write Go projects, if all routes are written in a .go file, as the number of routes increases, the routing file will become extremely bloated.

At this time, routes need to be grouped and managed in files, which is more conducive to code organization and maintenance.

Implementation of routing grouping

Original ungrouped file

func main() {
	router := gin.Default()
	router.POST("/one/a", OneAFunc)
	router.POST("/one/b", OneBFunc)
	router.POST("/one/c", OneCFunc)
	router.POST("/one/d", OneDFunc)
	router.POST("/one/e", OneEFunc)
	router.POST("/one/f", OneFFunc)
	router.POST("/one/g", OneGFunc)
	router.POST("/one/h", OneHFunc)
	router.POST("/one/i", OneIFunc)
	/*
		省略n条路由
	*/
	router.POST("/one/x", OneXFunc)
	router.POST("/one/y", OneYFunc)
	router.POST("/one/z", OneZFunc)

	router.POST("/two/a", TwoAFunc)
	router.POST("/two/b", TwoBFunc)
	router.POST("/two/c", TwoCFunc)
	router.POST("/two/d", TwoDFunc)
	router.POST("/two/e", TwoEFunc)
	router.POST("/two/f", TwoFFunc)
	router.POST("/two/g", TwoGFunc)
	router.POST("/two/h", TwoHFunc)
	router.POST("/two/i", TwoIFunc)
	/*
		省略n条路由
	*/
	router.POST("/two/x", TwoXFunc)
	router.POST("/two/y", TwoYFunc)
	router.POST("/two/z", TwoZFunc)
	router.Run(":8080")
}

Route logical grouping

We first create a routing group one with the prefix /one.

Then, the POST method is used to define multiple routing processing functions oneAFunc to oneZFunc in the routing group one, which handle the HTTP POST requests of the /two/a to /two/z routes respectively.

Request path

/one/a/

/tow/a/

	// 路由分组第一组
	one := router.Group("/one")
	{
		one.POST("/a", OneAFunc)
		one.POST("/b", OneBFunc)
		one.POST("/c", OneCFunc)
		one.POST("/d", OneDFunc)
		one.POST("/e", OneEFunc)
		one.POST("/f", OneFFunc)
		one.POST("/g", OneGFunc)
		one.POST("/h", OneHFunc)
		one.POST("/i", OneIFunc)
		/*
			省略n条路由
		*/
		one.POST("/x", OneXFunc)
		one.POST("/y", OneYFunc)
		one.POST("/z", OneZFunc)
	}
	//路由分组第二组
	two := router.Group("/two")
	{
		two.POST("/a", twoAFunc)
		two.POST("/b", twoBFunc)
		two.POST("/c", twoCFunc)
		two.POST("/d", twoDFunc)
		two.POST("/e", twoEFunc)
		two.POST("/f", twoFFunc)
		two.POST("/g", twoGFunc)
		two.POST("/h", twoHFunc)
		two.POST("/i", twoIFunc)
		/*
			省略n条路由
		*/
		two.POST("/x", twoXFunc)
		two.POST("/y", twoYFunc)
		two.POST("/z", twoZFunc)
	}

After grouping the go code, although the routes implement route grouping and the routing logic appears clear, the routing files are still concentrated in one file and the file is still large.

Routing is grouped and divided into files for management

File structure

main.go
handlers/
├── one_handlers.go
└── two_handlers.go

main.go

import (
	"github.com/gin-gonic/gin"
	"your_project/routers"
)

func main() {
    // 创建路由
	router := gin.Default()
    //
    routers.SetupRouter(router)
	// 路由分组第一组
	routers.TwoRoutersInit(router)
	// 路由分组第二组
	routers.OneRoutersInit(router)
    // 启动服务
	router.Run(":8080")
}

TwoRoutersInit() and OneRoutersInit() correspond to the following two files:

one_handlers.go

package routers

import "github.com/gin-gonic/gin"

func OneRoutersInit(engine *gin.Engine) {
	one := engine.Group("/one")
	{
		one.POST("/a", OneAFunc)
		one.POST("/b", OneBFunc)
		one.POST("/c", OneCFunc)
		one.POST("/d", OneDFunc)
		one.POST("/e", OneEFunc)
		one.POST("/f", OneFFunc)
		/*
			省略n条路由
		*/
		one.POST("/x", OneXFunc)
		one.POST("/y", OneYFunc)
		one.POST("/z", OneZFunc)
	}
}

func OneAFunc(context *gin.Context) {

}
/*
	省略N多方法
*/
func OneZFunc(context *gin.Context) {

}

two_handlers.go

package routers

import "github.com/gin-gonic/gin"

func TwoRoutersInit(engine *gin.Engine) {
	two := engine.Group("/two")
	{
		two.POST("/a", twoAFunc)
		two.POST("/b", twoBFunc)
		two.POST("/c", twoCFunc)
		two.POST("/d", twoDFunc)
		two.POST("/e", twoEFunc)
		two.POST("/f", twoFFunc)
		two.POST("/g", twoGFunc)
		two.POST("/h", twoHFunc)
		two.POST("/i", twoIFunc)
		/*
			省略n条路由
		*/
		two.POST("/x", twoXFunc)
		two.POST("/y", twoYFunc)
		two.POST("/z", twoZFunc)
	}
}

func twoAFunc(context *gin.Context) {

}
/*
省略n多方法
*/

func twoZFunc(context *gin.Context) {

}

Note: Each route must be placed in {}, and the method corresponding to each route has a fixed context *gin.Context, where the context wind state request and response

Official example:

main.go

// main.go
package main

import (
	"github.com/gin-gonic/gin"
	"your_project/routers"
)

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

	routers.SetupRouter(r)

	r.Run() // listen and serve on 0.0.0.0:8080
}

routers/router.go

package routers

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

func SetupRouter(r *gin.Engine) {
	v1 := r.Group("/v1")
	{
		v1.GET("/hello", func(c *gin.Context) {
			c.JSON(200, gin.H{
				"message": "hello",
			})
		})
	}

	v2 := r.Group("/v2")
	{
		v2.GET("/hello", func(c *gin.Context) {
			c.JSON(200, gin.H{
				"message": "hello v2",
			})
		})
	}
}

6. Middleware

1. Unified registration middleware

Register middleware uniformly for global routing or registered routing groups

r.Use(CheckToken())

2. Register middleware separately

userApi.POST("/upload",CheckToken(),userUpload)

3. Middleware function implementation

func CheckToken() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 验证不通过直接跳出
		//c.JSON(http.StatusBadRequest, gin.H{"msg": "need token"})
		//c.Abort()
		//return
		// 验证通过继续
		c.Next()
	}
}

Middleware scenario

Go Gin JWT

https://blog.csdn.net/u013302168/article/details/132178429

7. How to use go module to import packages in vscode

Go version management uses go module

(1) Suppose we now have two packages moduleA and moduleB


The moduleA package will import the moduleB package and use its New method.
(Note: Function names can only be quoted if they start with a capital letter)

The contents of moduleB/moduleB.go are as follows:

package moduleB

import "fmt"

func New(){
	fmt.Println("moduleB.New")
}

1. Discuss in two situations:

(1) Under the same project

Note: We can define multiple packages under one project.

The directory structure is as follows:

Insert image description here

Then create the mod of moduleA, open it in the integrated terminal, and enter it on the command line

go mod init moduleA

 

 

After execution, a mod.go file will be generated in the project root directory, and execute go mod tidy to install dependencies.

The content of moduleA’s go.mod is as follows:

module moduleA

go 1.17

The main.go content of moudleA is as follows:

package main

import (
    "fmt"
    "moduleA/moduleB"
)

func main() {
    moduleB.New()
    fmt.Println("main")
}

(2) Executed under different projects

Here we have completed the first step, and now we have come to the second step;

First, pull the code of moduleB out, and let’s implement references to packages under different projects.

At this time, there is no mod file in moduleB's package (folder), so you need to mod init yourself.

Open the terminal and enter at the command line

go mod init moduleB

At this time, moduleB’s go.mod should be:

module moduleA

go 1.17

require "moduleB" v0.0.0
replace "moduleB" => "../moduleB"


The moduleB.go file in moduleB does not move and is the same as above.


Because these two packages are not in the same project path, you want to import the local package, and these packages are not published to a remote github or other code repository address.

At this time we need to use the replace directive in the go.mod file.

In the caller, that is, moduleA/go.mod, specify the relative path to find the moduleB package as follows. At this time, the go.mod file of moduleA should be:

module moduleA

go 1.17

require "moduleB" v0.0.0
replace "moduleB" => "../moduleB"


The main.go file of moduleA should be:

package main

import (
    "fmt"
    "moduleB"
)func main() {
    moduleB.New()
    fmt.Println("main")
}


 

Guess you like

Origin blog.csdn.net/u013302168/article/details/132119952