GinGo is a language with a web framework. It is a similar martinibut API framework has better performance, due to the use httprouter, speed of nearly 40 times. If you are the performance and efficiency of suitors, you'll love Gin.

Gin framework introduced

Go world's most popular Web framework, Github have the 32K+star. Based httprouter Web development framework. Chinese documents complete, easy to use lightweight frame.

Installation and use of the frame Gin

installation

Download and install Gin:

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

Gin first example:

main Package 

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

FUNC main () { 
	// create a default routing engine 
	R & lt: gin.Default = () 
	// the GET: Request embodiment; / hello: route request 
	// when a client request method GET / hello path, anonymous function is executed later 
	r.get ( "/ hello", FUNC (gin.Context * c) { 
		// c.JSON: returns JSON format data 
		c .json (200, gin.H { 
			"the Message": "! the Hello world", 
		}) 
	}) 
	// start the HTTP service, default in 0.0.0.0:8080 start the service 
	r.Run () 
}

  

Save the above code and compile execute, and then use the browser to open 127.0.0.1:8080/helloyou can see a bunch of JSON string.

RESTful API

REST has nothing to do with technology, represent a style of software architecture, REST is Representational State Transfer abbreviation , the Chinese translation for "characterize the state transfer" or "presentation layer state transformation."

Recommended reading Ruan Yifeng understand RESTful architecture

Simply put, REST is the meaning when the interworking between the client and the Web server, using different protocols HTTP request methods of four representatives of the action.

  • GETFor access to resources
  • POSTNew Resource for
  • PUTUsed to update the resource
  • DELETETo delete the resource.

As long as the program follows the REST-style API, you can call RESTful API. Separating the rear end of the current architecture front to front and rear ends are substantially interact through RESTful API.

For example, we now want to write a book management system, we can search for a book to query, create, update and delete operations, we will design the client browser when writing programs interact with our Web server's ways and paths. According to our experience usually designed to the following pattern:

Request method URL meaning
GET /book Information inquiry book
POST /create_book Creating record books
POST /update_book Update books information
POST /delete_book Delete book information

Also according to the needs of our RESTful API design are as follows:

Request method URL meaning
GET /book Information inquiry book
POST /book Creating record books
PUT /book Update books information
DELETE /book Delete book information

Gin development framework to support the development of RESTful API.

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

	r.POST("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "POST",
		})
	})

	r.PUT("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "PUT",
		})
	})

	r.DELETE("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "DELETE",
		})
	})
}

  

Development of RESTful API's when we typically use Postman as a client test tool.

Gin rendering

HTML rendering

We first define a template file storage templatesfolder, then in its internal operations are defined in accordance with a postsfolder and a usersfolder. posts/index.htmlContents of the file are as follows:

{{define "posts/index.html"}}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>posts/index</title>
</head>
<body>

    {{.title}}

</body>
</html>
{{end}}

  

users/index.htmlContents of the file are as follows:

{{define "users/index.html"}}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>users/index</title>
</head>

<body>
    {{.title}}
</body>
</html>
{{end}}

  

Gin used in the framework LoadHTMLGlob()orLoadHTMLFiles() methods to render HTML template.

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("templates/**/*")
	//r.LoadHTMLFiles("templates/posts/index.html", "templates/users/index.html")
	r.GET("/posts/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "posts/index.html", gin.H{
			"title": "posts/index",
		})
	})

	r.GET("users/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "users/index.html", gin.H{
			"title": "users/index",
		})
	})

	r.Run(":8080")
}

  

Static file handling

When we render HTML file references static files, we only need to call in the following manner before rendering the page gin.Staticmethods can be.

func main() {
	r := gin.Default()
	r.Static("/static", "./static")
	r.LoadHTMLGlob("templates/**/*")
   // ...
	r.Run(":8080")
}

  

Supplement path processing

About path to the template files and static files, we need to be set according to the company / project. You can use the following function to get the current execution path of the program.

func getCurrentPath() string {
	if ex, err := os.Executable(); err == nil {
		return filepath.Dir(ex)
	}
	return "./"
}

  

Rendering JSON

main FUNC () { 
	R & lt: gin.Default = () 

	// gin.H a map [string] interface {} Abbreviation 
	r.get ( "/ someJSON", FUNC (C * gin.Context) { 
		// a manner : their splicing the JSON 
		c.JSON (http.StatusOK, gin.H { "Message": "! the Hello World"}) 
	}) 
	r.get ( "/ moreJSON", FUNC (C * gin.Context) { 
		// method II: use structure 
		var struct {MSG 
			the Name String JSON `:" User "` 
			the Message String 
			Age int 
		} 
		MSG.NAME = "prince" 
		msg.Message = "the Hello World!" 
		msg.Age = 18 is 
		c.JSON (HTTP .StatusOK, MSG) 
	}) 
	r.Run ( ": 8080") 
}

  

XML Rendering

Attention to the need to use the type of structure to be named.

main FUNC () { 
	R & lt: gin.Default = () 
	// gin.H a map [string] interface {} Abbreviation 
	r.get ( "/ someXML", FUNC (C * gin.Context) { 
		// a manner : their splicing the JSON 
		c.XML (http.StatusOK, gin.H { "Message": "! the Hello World"}) 
	}) 
	r.get ( "/ moreXML", FUNC (C * gin.Context) { 
		// method II: use structure 
		type struct {MessageRecord 
			the Name String 
			the Message String 
			Age int 
		} 
		var MSG MessageRecord 
		MSG.NAME = "prince" 
		msg.Message = "the Hello World!" 
		msg.Age = 18 is 
		c.XML (http.StatusOK, MSG ) 
	}) 
	r.Run ( ": 8080") 
}

  

YMAL rendering

r.GET("/someYAML", func(c *gin.Context) {
	c.YAML(http.StatusOK, gin.H{"message": "ok", "status": http.StatusOK})
})

  

protobuf rendering

r.get ( "/ someProtoBuf", FUNC (C * gin.Context) { 
	reps: = [] {Int64 Int64 (. 1), Int64 (2)} 
	label: = "Test" 
	// Protobuf specific definition written in testdata / protoexample file. 
	data: = {& protoexample.Test 
		the label: & label, 
		reps: reps, 
	} 
	// Note that the data in the response binary data becomes 
	// will be protoexample.Test protobuf output data sequence of the 
	c. protobuf (http.StatusOK, Data) 
})

  

Acquisition parameters

Get querystring parameters

querystringIt refers to the URL ?carried behind parameters, such as: /user/search?username=小王子&address=沙河. The method of obtaining the requested querystring parameters as follows:

main FUNC () { 
	// return a default the Default Routing Engine 
	R & lt: = gin.Default () 
	r.get ( "/ User / Search", FUNC (C * gin.Context) { 
		username: = c.DefaultQuery ( " username "," The little Prince ") 
		// username: = c.Query (" username ") 
		address: = c.Query (" address ") 
		// output json results to the caller 
		c.JSON (http.StatusOK, gin. {H 
			"Message": "OK", 
			"username": username, 
			"address": address, 
		}) 
	}) 
	r.Run () 
}

  

Get the form parameters

Data request to submit a form by the form, for example, to /user/searchsend a POST request, the request data acquisition mode as follows:

main FUNC () { 
	// the Default Routing Engine returns a default 
	= gin.Default (): R & lt 
	r.POST ( "/ User / Search", FUNC (C * gin.Context) { 
		while fail to values // DefaultPostForm returns the default value specified 
		// username: = c.DefaultPostForm ( "username ", " the little Prince") 
		username: = c.PostForm ( "username") 
		address: = c.PostForm ( "address") 
		// output json result to the caller 
		c.JSON (http.StatusOK, gin.H { 
			"Message": "OK", 
			"username": username, 
			"address": address, 
		}) 
	}) 
	r.Run ( ": 8080") 
}

  

Gets the path parameter

Parameter request URL path passing through, for example: /user/search/小王子/沙河. Acquisition parameters in the request URL path as follows.

main FUNC () { 
	// return a default the Default Routing Engine 
	R & lt: = gin.Default () 
	r.get ( "/ User / Search /: username /: address", FUNC (C * gin.Context) { 
		username: c.Param = ( "username") 
		address: = c.Param ( "address") 
		// outputs the result to the caller json 
		c.JSON (http.StatusOK, gin.H { 
			"Message": "OK", 
			"username ": username, 
			" address ": address, 
		}) 
	}) 

	r.Run (": 8080 ") 
}

  

Parameter binding

In order to more easily access request relevant parameters, to improve development efficiency, we can request based on Content-Typerequest identification data types and automatically extracts the request using reflection QueryString, form表单, JSON, XMLand other parameters of the structure. The following sample code demonstrates the .ShouldBind()powerful, it can be automatically extracted based on a request JSON, form表单and the QueryStringtype of data, and the value of the structure bound to the specified object.

// Binding from JSON
type Login struct {
	User     string `form:"user" json:"user" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

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

	// 绑定JSON的示例 ({"user": "q1mi", "password": "123456"})
	router.POST("/loginJSON", func(c *gin.Context) {
		var login Login

		if err := c.ShouldBind(&login); err == nil {
			fmt.Printf("login info:%#v\n", login)
			c.JSON(http.StatusOK, gin.H{
				"user":     login.User,
				"password": login.Password,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	}) 
		if err: = c.ShouldBind(&login); err == nil {

	// form form exemplary binding (User password = & q1mi = 123456) 
	router.POST ( "/ loginForm", FUNC (C * gin.Context) { 
		var the Login Login 
		// ShouldBind () will be selected according to self-tie Content-Type request Tuner 
		IF ERR: = c.ShouldBind (& Login); ERR == nil { 
			c.JSON (http.StatusOK, gin.H { 
				"User": login.User, 
				"password": login.Password, 
			}) 
		} the else { 
			c.JSON (http.StatusBadRequest, gin.H { "error": err.Error ()}) 
		} 
	}) 

	// QueryString exemplary binding (/ = q1mi & loginQuery User password = 123456)? 
	router.GET ( "/ loginForm ", FUNC (C * gin.Context) { 
		var the Login Login 
		// ShouldBind () will be selected according to bind request itself Content-Type 
			c.JSON (http.StatusOK, gin.H{
				"user":     login.User,
				"password": login.Password,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})

	// Listen and serve on 0.0.0.0:8080
	router.Run(":8080")
}

  

ShouldBindWill complete the binding sequence resolution request according to the data below: 1. If a GETrequest, using only Formthe binding engine ( query). 2. If a POSTrequest checks content-typewhether JSONor XMLbefore use Form( form-data).

File Upload

Single File Upload

main FUNC () { 
	Router: gin.Default = () 
	// Default processing multipart forms when memory limits are submissions MiB 32 
	// can be modified in the following manner 
	// router.MaxMultipartMemory = 8 << 20 // 8 MiB 
	router.POST ( "/ Upload", FUNC (C * gin.Context) { 
		// single file 
		file, ERR: = c.FormFile ( "file") 
		IF ERR = nil {! 
			c.JSON (http.StatusInternalServerError, Gin {.H 
				"Message": err.Error (), 
			}) 
			return 
		} 

		log.Println (file.Filename) 
		DST: fmt.Sprintf = ( "C: / tmp /% S", file.Filename) 
		// uploaded to the specified directory 
		c.SaveUploadedFile (File, DST) 
		c.JSON (http.StatusOK, gin.H { 
			"Message": fmt.Sprintf ( " '% S' uploaded the!", File.Filename),Filename),
		})
	})
	router.Run()
}

  

Upload multiple files

main FUNC () { 
	Router: gin.Default = () 
	// Default processing multipart forms when memory limits are submissions MiB 32 
	// can be modified in the following manner 
	// router.MaxMultipartMemory = 8 << 20 // 8 MiB 
	router.POST ( "/ Upload", FUNC (C * gin.Context) { 
		// form Multipart 
		form, _: = c.MultipartForm () 
		Files: form.File = [ "File"] 

		for index, File: = Range {files 
			log.Println (file.Filename) 
			DST: fmt.Sprintf = ( "C: / tmp / S_% D%", file.Filename, index) 
			// upload the file to the specified directory 
			c.SaveUploadedFile (file, dst ) 
		} 
		c.JSON (http.StatusOK, gin.H { 
			"Message": fmt.Sprintf ( "!% Files uploaded The D", len (Files)), 
		}) 
	}) 
	Router.Run()
}

  


Gin Middleware

Gin framework allows developers in processing request, the user's own added hook (Hook) function . The hook function is called middleware, middleware for dealing with some common business logic, such as login verification, log printing, time-consuming statistics.

Gin in the middleware must be a gin.HandlerFunctype . For example, we define as a middleware like the following code.

// StatCost is a statistical time-consuming request time-consuming middleware 
FUNC StatCost () {gin.HandlerFunc 
	return FUNC (c * gin.Context) { 
		Start: = Time.now () 
		c.Set ( "name", "small Prince ") 
		// perform other middleware 
		c.Next () 
		// Processed calculated 
		cost: time.Since = (Start) 
		log.Println (cost) 
	} 
}

  

Then the time of registration middleware, can be registered globally.

main FUNC () { 
	// create a default without any intermediate route 
	R & lt: gin.New = () 
	// a global register middleware 
	r.Use (StatCost ()) 
	
	r.get ( "/ Test", FUNC ( * gin.Context C) { 
		name:. = c.MustGet ( "name") (String) 
		log.Println (name) 
		c.JSON (http.StatusOK, gin.H { 
			"Message": "the Hello World!", 
		}) 
	}) 
	r.Run () 
}

  

Middleware can also be individually registered to a route.

// a / test2 registered route separate piece (can register multiple) 
	r.get ( "/ test2", StatCost (), FUNC (C * gin.Context) { 
		name: = c.MustGet ( "name"). (String) 
		log.Println (name) 
		c.JSON (http.StatusOK, gin.H { 
			"Message": "the Hello World!", 
		}) 
	})

  

Redirect

HTTP redirection

HTTP redirects is easy. Internal, external redirect support.

r.GET("/test", func(c *gin.Context) {
	c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})

  

Route Redirection

Routing redirection, use HandleContext:

r.GET("/test", func(c *gin.Context) {
    // 指定重定向的URL
    c.Request.URL.Path = "/test2"
    r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"hello": "world"})
})

  

Gin route

Normal routing

r.GET("/index", func(c *gin.Context) {...})
r.GET("/login", func(c *gin.Context) {...})
r.POST("/login", func(c *gin.Context) {...})

  

In addition, there is a request to match all of the methods Anyas follows:

r.Any("/test", func(c *gin.Context) {...})

  

Is not configured to add the routing processing function handler 404 by default it returns the code, the following code is not matched to the routing request return views/404.htmlpage.

r.NoRoute(func(c *gin.Context) {
		c.HTML(http.StatusNotFound, "views/404.html", nil)
	})

  

Routing Group

We can have a common URL prefix routed into one routing group. Habitual one pair {}routing parcel of the same group, just to look at clarity, do you need {}no difference to the package features.

func main() {
	r := gin.Default()
	userGroup := r.Group("/user")
	{
		userGroup.GET("/index", func(c *gin.Context) {...})
		userGroup.GET("/login", func(c *gin.Context) {...})
		userGroup.POST("/login", func(c *gin.Context) {...})

	}
	shopGroup := r.Group("/shop")
	{
		shopGroup.GET("/index", func(c *gin.Context) {...})
		shopGroup.GET("/cart", func(c *gin.Context) {...})
		shopGroup.POST("/checkout", func(c *gin.Context) {...})
	}
	r.Run()
}

  

Usually we will use to route packets when divided or partitioned business logic API version.

Routing principles

Gin frame routing using httprouter this library.

The basic principle is that a routing address prefix tree structure.