Net/HTTP standard library

Before we talk about the framework, let’s talk about the built-in NET/HTTP package for Go. In fact, NET/HTTP already provides a basic combination of routing functions and rich functionality. If you just need a simple API, NET/HTTP is sufficient. Let’s write the simplest Web service application:

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main(a) {
	// This method receives a string of route matches and a function of type func(ResponseWriter, *Request)
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(": 8000".nil)) // Listen on port 8000
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) Path Indicates the Path of the output URL
}
Copy the code

Browser access results:

Take a step further and see how common request parameter types are parsed and how to return json.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

func main(a) {
	// This method receives a string of route matches and a function of type func(ResponseWriter, *Request)
	http.HandleFunc("/", handler)
	http.HandleFunc("/get", handleGet)
	http.HandleFunc("/postJson", handlePostJson)
	http.HandleFunc("/postForm", handlePostForm)
	http.HandleFunc("/responseJson", handleResponseJson)
	log.Fatal(http.ListenAndServe(": 8000".nil)) // Listen on port 8000
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) Path Indicates the Path of the output URL
}

// Process GET request query parameters
func handleGet(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	id := query.Get("id")
	fmt.Fprintf(w, "GET: id=%s\n", id)
}

// Handle application/ JSON POST requests
func handlePostJson(w http.ResponseWriter, r *http.Request) {
	Create a JSON parser instance based on the request body
	decoder := json.NewDecoder(r.Body)
	// Store key=value data
	var params map[string]string
	// Parse parameters into map
	decoder.Decode(&params)
	fmt.Fprintf(w, "POST json: username=%s, password=%s\n", params["username"], params["password"])}// Handle application/x-www-form-urlencoded POST requests
func handlePostForm(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	username := r.Form.Get("username")
	password := r.Form.Get("password")
	fmt.Fprintf(w, "POST form-urlencoded: username=%s, password=%s\n", username, password)
}

// Returns the JSON data format
func handleResponseJson(w http.ResponseWriter, r *http.Request) {
	type Response struct {
		Code int         `json:"code"`
		Msg  string      `json:"msg"`
		Data interface{} `json:"data"`
	}
	res := Response{
		200."success"."admin",
	}
	json.NewEncoder(w).Encode(res) / / key
}
Copy the code

/get request result:

PostJson request result:

/postForm request result:

/responseJson request result:

At this point, basic usage has been covered, and more information about the NET/HTTP library can be found in the library documentation at studygolang.com/pkgdoc

Gin framework

Official introduction: Gin is an HTTP Web framework written in Go (Golang). It is an API framework similar to Martini but with better performance, nearly 40 times faster thanks to Httprouter. If you need great performance, use Gin.

Features:

  • Fast: Routing does not use reflection, Radix tree based, less memory footprint.
  • The middleware: HTTP requests can be processed by a series of middleware, such as Logger, Authorization, and GZIP. This feature is similar to NodeJsKoaThe frame is very similar. Middleware mechanisms also greatly improve the extensibility of the framework.
  • Exception handling: The service is always available and does not break down. Gin can catch panic and recover it. And there are extremely convenient mechanisms for handling errors that occur during HTTP requests.
  • JSONGin can parse and validate the requested JSON. This feature is very usefulRestful APIThe development of is especially useful.
  • Routing group: For example, group apis that need authorization and apis that do not need authorization, and group apis of different versions. And groups can be nested, and performance is not affected.
  • Built-in rendering: native support for JSON, XML and HTML rendering.

1. Start fast

package main

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

func main(a) {
	r := gin.Default()
	r.GET("/ping".func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() R.run (":9000") r.run (":9000")
}
Copy the code

Browser access:

Console output:

What does Gin do in this code?

  1. First of all, we usegin.Default()An instance is generated and assigned tor.
  2. Next, we user.Get("/ping", ...)Declares a route that tells Gin what URL can trigger the incoming function that returns the information we want to display in the user’s browser.
  3. In the end,r.Run()The default listening port is 8080. You can pass in the parameter setting port, for exampler.Run(":9999")It runs on port 9999.

2. The routing

The routing methods include GET, POST, PUT, PATCH, DELETE, OPTIONS, and Any, and can match Any of the above types of requests.

r.GET("/someGet".func)
r.POST("/somePost".func)
r.PUT("/somePut".func)
r.DELETE("/someDelete".func)
r.PATCH("/somePatch".func)
r.HEAD("/someHead".func)
r.OPTIONS("/someOptions".func)
// Handle all request methods
r.Any("/any".func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "Request Type": c.Request.Method,
    })
})
Copy the code

Routing parameters

/ / no parameters
r.GET("/".func (c *gin.Context)  {
	c.String(http.StatusOK, "Hello")})// Path parameter, matching /path/admin
r.GET("/path/:name".func(c *gin.Context) {
	name := c.Param("name")// Get the value of name in the URL path
	c.String(http.StatusOK, "Hello %s", name)
})
// Asterisk route parameters that match all, such as /all/*id, are not recommended
r.GET("/all/*id".func(c *gin.Context) {
	id := c.Param("id")
	c.String(http.StatusOK, "id is %s", id)
})
// Query parameters, matching user? Name =xxx&role= XXX, role is optional
r.GET("/user".func(c *gin.Context) {
	name := c.Query("name")// Query the value of the parameter name following the request URL
	role := c.DefaultQuery("role"."teacher")// If not, the default value "teacher" will be assigned.
	c.String(http.StatusOK, "%s is a %s", name, role)
})
/ / form form
r.POST("/form".func(c *gin.Context) {
	username := c.PostForm("username")
	password := c.DefaultPostForm("password"."000000") // You can set the default value
	c.JSON(http.StatusOK, gin.H{
		"username": username,
		"password": password,
	})
})
/ / json parameters
r.POST("/json".func(c *gin.Context) {
	type Body struct {
		Email    string `json:"email"`
		Username string `json:"username"`
	}
	var body Body
	c.ShouldBind(&body)// Bind parameters to parse them into the body structure
	c.JSON(http.StatusOK, body)
})
// Array parameters, matching multiple services such as array? Answer =xxx&answer=xxx&answer= XXX,key is the same, value is different
r.GET("/array".func(c *gin.Context) {
    array := c.QueryArray("answer")
	c.JSON(http.StatusOK, array)
})
// Map argument, dictionary argument, match map? ids[a]=123&ids[b]=456&ids[c]=789
r.GET("/map".func(c *gin.Context) {
	c.JSON(http.StatusOK, c.QueryMap("ids"))})Copy the code

Routing group

v1Group := r.Group("/v1")
{
    v1Group.GET("/user".func(c *gin.Context) {
        c.String(200."This is version V1 /v1/user.")
	})
}

v2Group := r.Group("/v2")
{
	v2Group.GET("/user".func(c *gin.Context) {
		c.String(200."This is version V2 /v2/user.")})}Copy the code

3. Output the render format

Gin can easily render the format of the output data

package main

import (
	"net/http"

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

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

	r.GET("/someString".func(c *gin.Context) {
		c.String(http.StatusOK, "string")})Gin.H is a shortcut to map[string]interface{}
	r.GET("/someJSON".func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "hey"."status": http.StatusOK})
	})

	r.GET("/moreJSON".func(c *gin.Context) {
		// You can also use a structure
		var msg struct {
			Name    string `json:"user"`
			Message string
			Number  int
		}
		msg.Name = "Lena"
		msg.Message = "hey"
		msg.Number = 123
		// Note that MSG.Name is changed to "user" in json due to 'json:"user".
		/ / will output: {" user ":" Lena ", "Message" : "hey", "Number" : 123}
		c.JSON(http.StatusOK, msg)
	})

	r.GET("/someXML".func(c *gin.Context) {
		c.XML(http.StatusOK, gin.H{"message": "hey"."status": http.StatusOK})
	})

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

	r.Run()
}
Copy the code

4. The middleware

Back to quick Start, we generated an instance using gin.Default(). Check the source code for gin

The Default function will bind two prepared middleware by Default, Logger and Recovery, to help us print log output and painc processing respectively. As you can see, Gin’s middleware is set up using the Use method, which receives a variable parameter, so we can set up multiple middleware at the same time.

A Gin middleware is actually a HandlerFunc defined by Gin

It is often used in our Gin, for example:

// func(c *gin.Context){} is a HandlerFunc function
r.GET("/".func(c *gin.Context) {
    c.String(200."hello")})Copy the code

Let’s now try out how to use middleware:

package main

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

func main(a) {
	// Create a router instance without middleware
	r := gin.New()

	// Use middleware globally
	// Use gin's own Logger Logger middleware
	r.Use(gin.Logger())
	// Recover from any panic using gin's own Recovery middleware, which will write a 500 error if panic occurs.
	r.Use(gin.Recovery())
    
    // The above code is equivalent to r := gin.Default()

	// Add custom global middleware
	r.Use(middleware.Cors())

	// Add middleware to a single route. You can add any number of middleware
	r.GET("/path", middleware.JWT())

	// Add middleware to the routing group. Middleware only takes effect in the routing group
	// user := r.Group("/user", middleware.AuthRequired())
	user := r.Group("/user")
	user.Use(middleware.AuthRequired())
	{
		user.POST("/login", Login)
	}
	r.Run()
}
Copy the code

After learning how to use middleware, how to customize your own middleware? It can be very simple, for example, if you want to customize a requestTime middleware to calculate the requestTime:

package main

import (
	"fmt"
	"time"

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

func main(a) {
	r := gin.Default()
	r.Use(requestTime())
	r.GET("/".func(c *gin.Context) {
		fmt.Println("Here I am hello")
		c.String(200."hello")
	})
	r.Run()
}

func requestTime(a) gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now() // Record the start time
		fmt.Println("I've got a hold of this place.")
		fmt.Println("I'm going to go now because I called c.next ().")
		c.Next() // Call the next HandlerFunc immediately (incurring call time)
		fmt.Println("I'm back again.")
		fmt.Println(time.Since(start)) // Prints the time difference between this request processing}}Copy the code

You can see that c.ext () plays a big role here, where operations before c.ext () are used for validation, such as whether access is allowed. The subsequent operations are usually used for summary processing, such as formatting output, response end time, response time calculation, and so on.

In gin middleware, in addition to c.next (), there is a corresponding c.abort (), whose role is to prevent subsequent processing functions, such as blocking subsequent operations if an illegal request is detected.

package main

import (
	"fmt"
	"net/http"

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

func main(a) {
	r := gin.Default()
	r.Use(authority())
	r.GET("/path/:name".func(c *gin.Context) {
		fmt.Println("Welcome")
		c.String(http.StatusOK, "Hello %s", c.Param("name"))
	})
	r.Run()
}

func authority(a) gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("Start - Permission Control")
		isAdmin := c.Param("name") = ="admin"
		if isAdmin {
			c.Next()
		} else {
			c.Abort() // The next HandlerFunc will not be executed
			c.String(http.StatusOK, "Sorry, you're not the administrator.")
		}
		fmt.Println("End - Access Control")}}Copy the code

Situation (1) :

Situation (2) :

After the program calls c.aport (), it will simply continue to execute the middleware code without jumping to the HandlerFunc interface, which will intercept it.

To summarize, c.next () executes the next HandlerFunc immediately and then jumps back to the next code, while c.abort () blocks the next HandlerFunc and only executes the next code, often used for permission control interception operations.

The value can be set using c. et(key, value), and the value can be set using C. et(key)

package main

import (
	"fmt"
	"net/http"

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

func main(a) {
	r := gin.Default()
	r.Use(authority())
	r.GET("/".func(c *gin.Context) {
		value, ok := c.Get("key")
		if ok {
			fmt.Println(value)
		}
		c.String(http.StatusOK, "Hello")
	})
	r.Run()
}

func authority(a) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set("key"."Hello")
		c.Next()
	}
}
Copy the code

Request result:

For the basic introduction, the documentation on GIN can be found at learnku.com/docs/gin-go…