1. 路由组
在 Gin
框架中,RouterGroup
结构体用于配置路由,RouterGroup
配置 HTTP
请求方法、路径与处理程序(以及中间件)之间的关联关系。
字段 RouterGroup.Handlers
保存该组路由所有的中间件处理程序,通过 RouterGroup.engine
的 addRoute
方法,把路径、HTTP
请求方法和处理程序(含中间件)的路由信息写入到对应 HTTP
方法的路由信息表。
在 Gin
中,在路由中引入了组的概念。使用
Group(relativePath string, handlers ...HandlerFunc)
方法可以增加分组,第一个参数作为整个组的基础路径,第二个参数可选加入适用于本组的中间件。路由分组的目的是为了方便 URL
路径的管理。
func loginEndpoint(c *gin.Context) {
c.JSON(http.StatusOK, "login")
}
func submitEndpoint(c *gin.Context) {
c.JSON(http.StatusOK, "submit")
}
func readEndpoint(c *gin.Context) {
c.JSON(http.StatusOK, "read")
}
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.GET("/login", loginEndpoint)
v1.GET("/submit", submitEndpoint)
v1.GET("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.GET("/login", loginEndpoint)
v2.GET("/submit", submitEndpoint)
v2.GET("/read", readEndpoint)
}
router.Run(":8080")
}
分别输入
curl http://127.0.0.1:8080/v1/login
curl http://127.0.0.1:8080/v2/login
运行输出:
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /v1/login --> main.loginEndpoint (3 handlers)
[GIN-debug] GET /v1/submit --> main.submitEndpoint (3 handlers)
[GIN-debug] GET /v1/read --> main.readEndpoint (3 handlers)
[GIN-debug] GET /v2/login --> main.loginEndpoint (3 handlers)
[GIN-debug] GET /v2/submit --> main.submitEndpoint (3 handlers)
[GIN-debug] GET /v2/read --> main.readEndpoint (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2022/07/14 - 15:03:41 | 200 | 56.755µs | 127.0.0.1 | GET "/v1/login"
[GIN] 2022/07/14 - 15:03:45 | 200 | 30.819µs | 127.0.0.1 | GET "/v2/login"
通过组路由的方式可以更好地对 URL
进行管理,比如可以按照业务来分类管理,按照版本管理。
当然也可以不使用组路由,直接使用全局路由。如:router.GET("/login", Login)
也是可以的。
2. 自定义校验
package main
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"`
}
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
date, ok := fl.Field().Interface().(time.Time)
if ok {
today := time.Now()
if today.After(date) {
return false
}
}
return true
}
func main() {
route := gin.Default()
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
}
route.GET("/bookable", getBookable)
route.Run(":8085")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
输出结果:
$ curl "localhost:8085/bookable?check_in=2118-04-16&check_out=2118-04-17"
{"message":"Booking dates are valid!"}
$ curl "localhost:8085/bookable?check_in=2118-03-10&check_out=2118-03-09"
{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"}