banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

[Reprint] Gin Routing of the Web Framework Gin

title: 【Reprint】Web Framework Gin and Gin Routing
date: 2021-08-16 09:30:01
comment: false
toc: true
category:

  • Golang
  • Gin
    tags:
  • Reprint
  • Go
  • Golang
  • Gin
  • Web
  • Framework
  • Routing

This article is reprinted from: Web Framework Gin | Gin Routing


Web Framework Gin | Gin Routing#

Gin is a standard web service framework that follows the Restful API interface specification, and its routing library is implemented based on httproute.

This section will start with Gin routing and detail how to implement various routing scenarios using Gin.

Basic Routing#

Gin supports request types such as GET, POST, PUT, PATCH, DELETE, OPTIONS, etc.

Example

package main

import (
	"net/http"

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

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

	// Set up a GET request with the URL /hello and implement a simple response
	route.GET("/get", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "this is a get method response!",
		})
	})

	// A specific implementation can be defined in a separate function
	route.POST("/post", postHandler)

	route.PUT("/put", func(c *gin.Context) {

	})

	route.PATCH("/patch", func(c *gin.Context) {

	})

	route.DELETE("/delete", func(c *gin.Context) {

	})

	// ……

	route.Run()
}

func postHandler(c *gin.Context) {
	c.JSON(http.StatusOK, "this is a post method response!")
}

Parameter Handling#

Path Parameters#

You can obtain parameters from the request path using the Param function of *gin.Context. Path parameters start with :, for example: /user/:name, which can match the path /user/xx.

Example

package main

import (
	"fmt"
	"net/http"

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

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

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

	users := []User{
		{
			Username: "xcbeyond",
			Sex:      "male",
			Age:      18,
			Labels:   []string{"young", "handsome"},
		},
	}

	// Request path contains parameters
	route.GET("/user/:name", func(c *gin.Context) {
		// Get parameters from the request path
		name := c.Param("name")
		for _, user := range users {
			if user.Username == name {
				c.JSON(http.StatusOK, user)
				return
			}
		}
		c.JSON(http.StatusOK, fmt.Errorf("not found user [%s]", name))
	})

	route.Run()
}

Query Parameters#

You can obtain parameters using the Query function of *gin.Context, for example: /user?name=xcbeyond.

Example

package main

import (
	"net/http"

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

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

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

	users := []User{
		{
			Username: "xcbeyond",
			Sex:      "male",
			Age:      18,
			Labels:   []string{"young", "handsome"},
		},
	}

	// Query parameters, for example: /user?name=xcbeyond
	route.GET("/user", func(c *gin.Context) {
		// Get parameters from the query
		name := c.Query("name")
		for _, user := range users {
			if user.Username == name {
				c.JSON(http.StatusOK, user)
				return
			}
		}
		c.JSON(http.StatusOK, "not found user "+name)
	})

	route.Run()
}

……#

Route Grouping#

Gin provides the ability to group routes, making it easier to manage grouped routes by categorizing them based on the same route URL prefix, commonly seen in different version groups, such as: /api/v1, /api/v1.

In addition, it also supports multi-level grouping.

Example

package main

import (
	"math/rand"
	"net/http"

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

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

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

	users := []User{
		{
			Username: "xcbeyond",
			Sex:      "male",
			Age:      18,
			Labels:   []string{"young", "handsome"},
		},
		{
			Username: "niki",
			Sex:      "female",
			Age:      16,
			Labels:   []string{"pretty"},
		},
	}

	// API group
	api := route.Group("/api")
	{
		// v1 group
		v1 := api.Group("/v1")
		{
			v1.GET("/user/:name", func(c *gin.Context) {
				name := c.Param("name")
				for _, user := range users {
					if user.Username == name {
						c.JSON(http.StatusOK, user)
						return
					}
				}
				c.JSON(http.StatusOK, "not found user :"+name)
			})
		}

		// v2 group
		v2 := api.Group("/v2")
		{
			v2.GET("/user/:name", func(c *gin.Context) {
				name := c.Param("name")
				for _, user := range users {
					if user.Username == name {
						c.JSON(http.StatusOK, user)
						return
					}
				}

				// If not found, return a random one
				user := users[rand.Intn(len(users)-1)]
				c.JSON(http.StatusOK, "not found user ["+name+"],but user ["+user.Username+"] is exist!")
			})
		}
	}

	route.Run()
}

Route Splitting#

In the above routing examples, all routing information is written in the same source file and function, but in actual projects, there will be a large number of interfaces, and writing them together is not appropriate.

In actual projects, we prefer to split the routing code, which can be separated into individual packages, multiple routing source files, etc.

Depending on the size of the actual project, it can be split into different granularities.

(The following route splitting is for reference only and can be flexibly adjusted according to specific projects!)

Split Routes into Separate Source Files or Packages#

Separate the routing implementation into a separate package to make the project structure clearer. The project structure is as follows:

.
├── main.go
└── routes
    └── routes.go

Example complete source code: route-split-v1

Implement and register routing information in the /routes/routes.go file:

package routes

import (
	"net/http"

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

type User struct {
	Username string   `json:"username"`
	Sex      string   `json:"sex"`
	Age      int      `json:"age"`
	Labels   []string `json:"lalels"`
}

var users []User

// For convenience in testing, directly assign values through init. In actual projects, data is generally queried from databases or other sources.
func init() {
	users = []User{
		{
			Username: "xcbeyond",
			Sex:      "male",
			Age:      18,
			Labels:   []string{"young", "handsome"},
		},
		{
			Username: "niki",
			Sex:      "female",
			Age:      16,
			Labels:   []string{"pretty"},
		},
	}
}

// SetupRouter configures the routes
func SetupRouter() *gin.Engine {
	route := gin.Default()

	route.GET("/user/:name", querUserHandler)
	// Other more routes

	return route
}

// handler
func querUserHandler(c *gin.Context) {
	name := c.Param("name")
	for _, user := range users {
		if user.Username == name {
			c.JSON(http.StatusOK, user)
			return
		}
	}
	c.JSON(http.StatusOK, "not found user :"+name)
}

And call the route configuration function SetupRouter in main.go:

func main() {
	route := routes.SetupRouter()
	if err := route.Run(); err != nil {
		fmt.Printf("startup server failed,err: %v", err)
	}
}

Split Routes into Multiple Source Files#

As the project's business functionality becomes richer and larger, writing all routes in a single routes.go source file will lead to this source file becoming increasingly bloated, making it inconvenient for later maintenance and reading.

Therefore, routes can be split into multiple routing files based on certain dimensions to implement different business functionalities. The project structure is as follows:

.
├── main.go
└── routes
    ├── auth.go
    ├── routes.go
    └── user.go

Example complete source code: route-split-v2

In the routes package, split into multiple routing implementation files based on certain dimensions, such as by business module, which can be split into authentication module (auth.go), user module (user.go), etc., and implement specific business functionalities in their respective routing files, and register the routes.

For example, in the authentication module auth.go:

package routes

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

// AuthRegister route register
func AuthRegister(e *gin.Engine) {
	e.GET("/auth/login", loginHandler)
	e.POST("/auth/logout", logoutUserHanler)
	// ……
}

// loginHandler
func loginHandler(c *gin.Context) {

}

// logoutUserHanler
func logoutUserHanler(c *gin.Context) {

}

Here, the AuthRegister function is defined to register all routes under this module. Note that this function starts with an uppercase letter, making it a global function that can be called from outside the package in main.go.

In routes/routes.go, configure the routes and uniformly register all module routes:

package routes

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

// SetupRouter configures the routes
func SetupRouter() *gin.Engine {
	route := gin.Default()

	// other config

	// register all routes.
	UserRegister(route)
	AuthRegister(route)
	// ……

	return route
}

main.go is the same as the previous version, serving as the entry point of the program:

func main() {
	route := routes.SetupRouter()
	if err := route.Run(); err != nil {
		fmt.Printf("startup server failed,err: %v", err)
	}
}

References:

  1. api-examples
  2. Gin Framework Route Splitting and Registration
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.