⽅便⼀部分相同的URL的管理.
(1). 场景:
①. 划分业务逻辑,公共前缀的管理.
②. 划分API版本区分.
1. 路由分组:
(1). api.test.com/topic/main.go:
import "topic.test.com/src" // moduel/文件名
func main() {
router := gin.Default()
// 定义一个路由分组
v1 := router.Group("/v1/topics")
// {}不是强关联,只是为了美观,做代码切割,不写也可以,有作用域的区别
{
// 什么都不写,表示与组的路由是一致的(GET /v1/topics)
v1.GET("", src.GetList) // 将业务代码分离出去
v1.GET("/:topic_id", func(context *gin.Context) {
context.String(http.StatusOK, "获取%s的帖子", context.Param("topic_id"))
})
// 在这个下面所有定义的路由都需要鉴权
v1.Use(src.MustLogin())
{
v1.POST("", src.AddTopic)
}
}
router.Run()
}
注:
①. 如果src.GetList(),不想写src的话.需要这样引入 => . "topic.test.com/src"
②. 不能写src.GetList(),否则是表示执行函数得到的结果.
③. 如果想写src.GetList(),则需要改造如下:
func GetList2() gin.HandlerFunc {
return func (c *gin.Context) {
//
}
}
④. v1.Use的参数为HandlerFunc,所以是执行函数返回结果.
⑤. 为了美观,可以在路由上加一个{}代码块(代码块有作用域),不是强关联,只是为了美观.
(2). api.test.com/topic/src/TopicDao.go:
src是所有业务代码
func MustLogin() gin.HandlerFunc {
return func (c *gin.Context) {
if _, status := c.GetQuery("token");!status {
c.String(http.StatusUnauthorized, "授权失败")
c.Abort() // 没有的话,会继续往下执行
} else {
c.Next()
}
}
}
func GetList(c *gin.Context) {
// if c.Query("username") == "" {
// c.String(http.StatusOK, "获取所有帖子")
// } else {
// c.String(http.StatusOK, "获取%s所有帖子", c.Query("username"))
// }
// get参数校验
query := TopicQuery{}
err := c.BindQuery(&query) // 是BindQuery
if err != nil {
c.JSON(500, "参数错误") // 自动报一个400 Bad Request的错误,写成500也是400
} else {
c.JSON(http.StatusOK, query)
}
}
func AddTopic(c *gin.Context) {
// c.JSON(http.StatusOK, CreateTopic(1, "PHP"))
// post参数校验
query := Topic{}
err := c.BindJSON(&query) // 是BindJSON
if err != nil {
c.JSON(500, "参数错误")
} else {
c.JSON(http.StatusOK, query)
}
}
注:
①. Dao层操作数据库实体、redis实体.
②. 路由测试:
POST localhost:8080/v1/topics => 结果是:授权失败
POST localhost:8080/v1/topics?token=123 => 结果是:{ "id": 1, "title": "PHP" }
如果没有json映射字段则返回:{ "TopicId": 1, "TopicTitle": "PHP" }
③. 参数绑定Model:
a. GetList中,如果参数有username、page、pagesize等,用if来判断的话,非常繁琐.
b. 可以将get参数绑定(get参数映射为一个模型).
c. 很多流行框架支持参数绑定Model(传过来的参数与model进行绑定).
④. GET localhost:8080/v1/topics?username=david&page=1&pagesize=10
=> 如果TopicQuery struct中没有form字段会是{ "username": "", "page": 0, "pagesize": 0 }
localhost:8080/v1/topics?username=david&page=1
=> { "username": "david", "page": 1, "pagesize": 0 }
localhost:8080/v1/topics?username=david&page=1&pagesize=10
=> { "username": "david", "page": 1, "pagesize": 10 }
localhost:8080/v1/topics?username=david&pagesize=10
=> 参数错误
localhost:8080/v1/topics?username=david&page=0
=> 参数错误,page为0
localhost:8080/v1/topics?username=david&page=""
=> 参数错误,page为""
(3). api.test.com/topic/src/TopicModel.go:
package src
type Topic struct {
TopicId int `json:"id"` // json反射的映射字段
TopicTitle string `json:"title" binding:"min=4,max=20"` // 在4和20字之间,中英文都是一样
TopicShortTitle string `json:"stitle" binding:"requried,nefield=TopicTitle"` // 不能与TopicTitle一样
UserIP string `json:"ip" binding:"ipv4"`
TopicScore int `json:"score" binding:"omitempty,gt=5"` // 可以不填,填了就大于5
TopicUrl string `json:"url" binding:"omitempty,topicurl"`
}
func CreateTopic (id int, title string) Topic {
return Topic{id, title}
}
// query参数绑定
type TopicQuery struct {
UserName string `json:"username" form:"username"`
Page int `json:"page" form:"page" binding:"required"`
PageSize int `json:"pagesize" form:"pagesize"`
}
注:
①. form决定了绑定query参数的key到底是什么.没写form,则不会绑定.
②. 内置验证器是第三方库github.com/go-playground/validator