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.
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
.
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
.
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.
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: