如果全都在main.go中处理请求,会导致文件过于混乱且开发效率低下。下面介绍如何将路由分组,并将请求处理代码放在同一文件夹下的不同的文件中,方便统一管理。
改进前
改进前的主程序大概是这样的,所有路由处理函数都在func main()里,十分臃肿。
package main
import (
"encoding/xml"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
type LoginData struct {
User string `form:"user" xml:"user" binding:"required"`
Password string `form:"password" xml:"password" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/post/xml", func(c *gin.Context) {
xmlSliceData, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": err.Error(),
})
}
data := LoginData{}
if err := xml.Unmarshal(xmlSliceData, &data); err == nil {
fmt.Printf("%#v\n", data)
if data.User == "root" && data.Password == "123456" {
c.JSON(200, gin.H{"status": "you are logged in"})
} else {
c.JSON(401, gin.H{"status": "unauthorized"})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": err.Error(),
})
}
})
router.POST("/post/baidu", func(c *gin.Context) {
searchContent := c.PostForm("content")
c.JSON(http.StatusOK, gin.H{
"content": searchContent,
})
})
err := router.Run(":8080")
if err != nil {
return
}
}
改进后
创建路由的主程序,按照下面的方式,就能把主程序和各个组别的路由处理程序分离开来。注意导包路径。
可以看到这边使用了router包下的TestRouter函数,这个函数的作用是绑定路由,具体实现见后文的代码。
package main
import (
"github.com/gin-gonic/gin"
"learnGIN/router"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("views/*")
// testRouter.go
router.TestRouter(r)
err := r.Run(":8080")
if err != nil {
return
}
}
下面是包含路由信息的router文件,在TestRouter中,绑定了test组路由执行的函数,注意访问特定组路由的时候,需要加上组的路径。就下面的例子的login来说,需要访问http://localhost:8080/test/login
。
package router
import (
"encoding/xml"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
type LoginData struct {
User string `form:"user" xml:"user" binding:"required"`
Password string `form:"password" xml:"password" binding:"required"`
}
func TestRouter(router *gin.Engine) {
testRouter := router.Group("/test")
testRouter.GET("/login", func(c *gin.Context) {
data := LoginData{}
if c.ShouldBind(&data) == nil {
if data.User == "root" && data.Password == "123456" {
c.JSON(200, gin.H{"status": "you are logged in"})
} else {
c.JSON(401, gin.H{"status": "unauthorized"})
}
}
})
testRouter.GET("/html", func(c *gin.Context) {
c.HTML(http.StatusOK, "baidu.html", gin.H{
"title": "fake baidu",
})
})
testRouter.POST("/baidu", func(c *gin.Context) {
searchContent := c.PostForm("content")
c.JSON(http.StatusOK, gin.H{
"content": searchContent,
})
})
testRouter.POST("/xml", func(c *gin.Context) {
xmlSliceData, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": err.Error(),
})
}
data := LoginData{}
if err := xml.Unmarshal(xmlSliceData, &data); err == nil {
fmt.Printf("%#v\n", data)
if data.User == "root" && data.Password == "123456" {
c.JSON(200, gin.H{"status": "you are logged in"})
} else {
c.JSON(401, gin.H{"status": "unauthorized"})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest,
"msg": err.Error(),
})
}
})
}
控制器
在上一步的基础上,我们能够继续改进项目框架,形成我们现在主流的后端处理服务的模式。这边将router中的代码进一步分离,分成了router和controller两个部分。使router中的代码进一步精简,同时在controller控制器中,只需要专注于实现业务逻辑。
这是当前的项目架构。
下面是router代码。
package router
import (
"github.com/gin-gonic/gin"
"learnGIN/myserver/controller"
)
func TestRouter(router *gin.Engine) {
testRouter := router.Group("/test")
testRouter.GET("/login", controller.TestController{}.Login)
testRouter.GET("/html", controller.TestController{}.Html)
testRouter.POST("/baidu", controller.TestController{}.Baidu)
testRouter.POST("/xml", controller.TestController{}.Xml)
}
下面是控制器代码
package controller
import (
"encoding/xml"
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
type TestController struct {
BaseController
}
type LoginData struct {
User string `form:"user" xml:"user" binding:"required"`
Password string `form:"password" xml:"password" binding:"required"`
}
func (t TestController) Login(c *gin.Context) {
data := LoginData{}
if err := c.ShouldBind(&data); err == nil {
log.Printf("%#v\n", data)
if data.User == "root" && data.Password == "123456" {
t.Success(c, gin.H{"status": "you are logged in"})
} else {
t.Success(c, gin.H{"status": "unauthorized"})
}
} else {
t.Fail(c, gin.H{"error": err.Error()})
}
}
func (t TestController) Html(c *gin.Context) {
c.HTML(http.StatusOK, "baidu.html", gin.H{
"title": "fake baidu",
})
}
func (t TestController) Baidu(c *gin.Context) {
searchContent := c.PostForm("content")
t.Success(c, gin.H{
"content": searchContent,
})
}
func (t TestController) Xml(c *gin.Context) {
xmlSliceData, err := c.GetRawData()
if err != nil {
t.Fail(c, gin.H{
"code": http.StatusBadRequest,
"msg": err.Error(),
})
}
data := LoginData{}
if err := xml.Unmarshal(xmlSliceData, &data); err == nil {
fmt.Printf("%#v\n", data)
if data.User == "root" && data.Password == "123456" {
t.Success(c, gin.H{"status": "you are logged in"})
} else {
t.Success(c, gin.H{"status": "unauthorized"})
}
} else {
t.Fail(c, gin.H{
"code": http.StatusBadRequest,
"msg": err.Error(),
})
}
}
上面的controller继承自BaseController类,继承了Success和Fail函数。
下面是BaseController的代码。
package controller
import "github.com/gin-gonic/gin"
type BaseController struct{}
func (b *BaseController) Success(c *gin.Context, data gin.H) {
c.JSON(200, data)
}
func (b *BaseController) Fail(c *gin.Context, data gin.H) {
c.JSON(200, data)
}