初始化路由 原创
说明
从本节课开始,我们会逐渐把项目按目标项目结构来调整。
这个节的目标如下:
- 创建 initialize 包
- 初始化服务
- 初始化路由
- 注册 api 路由
1、创建 initialize 包
我们在程序结构中有一个 initialize
包,这个包的作用是进行一些初始化操作。
首先,在代码根目录中创建 initialize
文件夹,再在里面创建一个 server.go
文件,填写代码如下:initialize/server.go
package initialize
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 初始化
func NewServer() *gin.Engine {
r := gin.New()
r.Use(gin.Logger(), gin.Recovery())
// 注册一个路由
r.GET("/", func(c *gin.Context) {
// 以 JSON 格式响应
c.JSON(http.StatusOK, gin.H{
"Data": "Hello World!",
})
})
return r
}
2、初始化服务
上面只是简单的把之前在 main.go
中的代码拷贝过来,并没有做太多的解耦,为了让代码看起来更合理,我们将initialize/server.go
的代码修改为如下:
package initialize
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 初始化
type Engine struct {
*gin.Engine
Mode string
}
func NewEngine() *Engine {
g := &Engine{}
gin.SetMode(g.Mode)
g.injectMiddlewares()
g.injectRouters()
return g
}
func (s *Engine) injectMiddlewares() {
g := gin.New()
defer func() {
s.Engine = g
}()
if s.Mode == gin.TestMode {
return
}
// 注册中间件
g.Use(gin.Logger())
g.Use(gin.Recovery())
}
func (s *Engine) injectRouters() {
r := s.Engine
// 注册一个路由
r.GET("/", func(c *gin.Context) {
// 以 JSON 格式响应
c.JSON(http.StatusOK, gin.H{
"Data": "Hello World!",
})
})
s.Engine = r
}
然后将 main.go
文件改造成如下:
package main
import (
"log"
"net/http"
"time"
"github.com/joker-bai/hawkeye/initialize"
)
func main() {
// 初始化
engine := initialize.NewEngine()
server := &http.Server{
Addr: ":8080",
WriteTimeout: time.Second * 3600,
ReadTimeout: time.Second * 3600,
IdleTimeout: time.Second * 5 * 60,
Handler: engine,
}
// 运行服务
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Failed to start http server, error: %s", err)
}
}
现在我们不是直接使用 gin.Engine.Run()
来启动服务,而是直接使用 net/http
来启动,主要是为了定义更多的参数。
现在,我们可以重启项目,再次使用 apifox 测试,观察是否有问题。
3、初始化路由
在之前的代码中,我们定义了 /
路由,如果整个项目都是这么一个简单的路由,其实放在任何地方都无所谓。
但是,在真实的项目中,往往有许许多多的路由,为此,我们需要将路由拆分出来,方便管理。
首先,创建 initialize/router.go
文件,写入以下内容:
package initialize
import (
"net/http"
"github.com/gin-gonic/gin"
)
func (s *Engine) injectRouterGroup(router *gin.RouterGroup) {
public := router.Group("/api")
{
// 注册一个路由
public.GET("/", func(c *gin.Context) {
// 以 JSON 格式响应
c.JSON(http.StatusOK, gin.H{
"Data": "Hello World!",
})
})
}
}
然后将 initialize/server.go
中的injectRouters
方法改造成如下:
func (s *Engine) injectRouters() {
r := s.Engine
apiRouter := r.Group("")
s.injectRouterGroup(apiRouter)
s.Engine = r
}
在initialize/router.go
文件中,我用了组的概念,并且组下面的成员都必须以 /api
作为 URI 前缀,如果我们还是以http://127.0.0.1:8080/
进行访问的话,会报 404,如下:
这时候我们应该访问http://127.0.0.1:8080/api/
,如下:
4、注册 api 路由
上面代码是直接将路由以及处理的业务都放在了 initialize/router.go 文件夹中,而在实际开发中,它们是分开的。
首先,在 internal/app/controllers/api/v1
中创建helloworld.go
文件,写入以下代码:
package v1
import (
"net/http"
"github.com/gin-gonic/gin"
)
type HelloWorldController struct{}
func (s *HelloWorldController) Get(ctx *gin.Context) {
ctx.JSON(
http.StatusOK, gin.H{
"Data": "Hello World",
},
)
}
这段代码是处理请求的业务逻辑。
然后,再在 internal/app/routers
中创建 helloworld.go
文件,写入以下内容:
package routers
import (
"github.com/gin-gonic/gin"
v1 "github.com/joker-bai/hawkeye/internal/app/controllers/api/v1"
)
type HelloWorldRouter struct{}
// Inject 实现inject接口
func (r *HelloWorldRouter) Inject(router *gin.RouterGroup) {
hc := new(v1.HelloWorldController)
router.GET("/hello", hc.Get)
}
这段代码主要是实现 inject
接口,注册具体的业务路由。
最后,再修改 initialize/router.go
文件中的代码,内容如下:
package initialize
import (
"github.com/gin-gonic/gin"
"github.com/joker-bai/kubemana/internal/app/routers"
)
type injector interface {
Inject(router *gin.RouterGroup)
}
func (s *Engine) injectRouterGroup(router *gin.RouterGroup) {
{
for _, r := range []injector{
new(routers.HelloWorldRouter),
} {
r.Inject(router.Group("/api"))
}
}
}
这段代码就是把业务路由加入路由组里。
5、测试一下
上面我们已经把所有代码改造完成,接下来就测试一下代码运行是否能达到预期。
启动项目过后,使用apifox
进行测试,结果符合预期,如下:
我们的请求地址变成了http://127.0.0.1:8080/api/hello。
到现在,我们整个业务流转逻辑基本完成,如下:
这里还没有数据处理相关的逻辑,后续再加上。
6、代码版本
这节开发完成后,给代码打一个版本标记,如下:
git add .
git commit -m "初始化路由"