1.背景介绍

使用Go开发Web API框架,验证学习成果。在实践中学习。

2.目录介绍

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
├── README.md
├── app
│   ├── app.go //主程序文件
│   ├── common // 公共文件
│   │   ├── base_controller.go // 控制器基类
│   │   ├── base_model.go // 模型基类
│   │   ├── error.go // 错误处理
│   │   ├── global.go // 全局变量
│   │   ├── response_code.go // api返回code定义
│   │   └── router.go // 路由解析
│   ├── config // 配置目录
│   │   ├── dev.ini // 测试环境配置(默认加载)
│   │   ├── local.ini // 本地环境配置
│   │   └── mysql_config.go
│   ├── controllers // 控制器目录
│   │   ├── v1 
│   │   └── v2
│   ├── models // 数据库模型文件
│   │   └── user.go
│   └── static // 静态资源目录
│       └── a.png
├── go.mod
├── go.sum
├── main.go // 启动文件
└── test // 测试目录

3. 启动流程

image-20210222151006966

4.路由设计

框架设计的路由支持Api多版本,以及版本间的兼容性。

4.1 期望效果

image-20210222153003507

4.2 定义路由结构体

1
2
3
4
5
// 定义路由储存组
type RouteList struct {
	//Route map[版本][控制器]处理器
	Route map[string]map[string]interface{}
}

4.3 添加注册路由方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
 * @description: 注册路由
 * @user: Shershon
 * @receiver receiver RouteConfig
 */
func (receiver *RouteList) AddRoute(version, pattern string, controller interface{}) {
	if receiver.Route[version] == nil {
		receiver.Route[version] = make(map[string]interface{})
	}
	receiver.Route[version][pattern] = controller
}

4.4 路由注册

在app.go主程序文件中,通过import _ 包路径的方式,执行控制器包中的init(),从而达到自动注册路由。

文件: app/app.go

1
2
3
4
5
6
7
package app
import (
	...
	_ "goe/app/controllers/v1"
	_ "goe/app/controllers/v2"
  ...
)

文件: app/controller/v1/Test.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
 * @Author Shershon
 * @Description V1版本,Test控制器
 * @Date 2021/2/19 4:21 下午
 **/
package v1
import "goe/app/common"
type TestController struct {
	common.BaseController
}
func init() {
  // 注册路由
	common.RouteListInstance.AddRoute("v1","test",&TestController{})
}
func (t TestController) Hello() error {
	return t.Error("v1 hello")
}
func (t TestController) Run() error {
	return t.Error("v1 Run")
}

文件: app/controller/v2/Test.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/**
 * @Author Shershon
 * @Description V2版本,Test控制器
 * @Date 2021/2/19 4:21 下午
 **/
package v2
import "goe/app/common"
type TestController struct {
	common.BaseController
}
func init() {
	common.RouteListInstance.AddRoute("v2","test",&TestController{})
}
func (t TestController) Hello() error  {
	return t.Error("v2 hello")
}

4.4 路由解析

每个Http请求,最终都会被ServeHTTP处理。

文件: app/common/router.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/**
 * @description: 处理接收请求
 * @user: Shershon
 * @receiver receiver
 * @param w
 * @param r
 */
func (receiver *RouteList) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// 初始化业务类属性
	BusErrorInstance.Response = w
	// 捕获请求过程中的错误
	defer BusErrorInstance.CatchError()
	// 静态路由解析
	isStaticReq := staticForWard(w, r)
	if !isStaticReq {
		// 动态路由解析
		routeForWard(w, r)
	}
}
1.静态路由解析
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/**
 * @description: 静态路由解析
 * @user: Shershon
 * @param w
 * @param r
 * @return bool
 * @date 2021-02-22 11:36:37
 */
func staticForWard(w http.ResponseWriter, r *http.Request) bool {
	if strings.HasPrefix(r.URL.String(), "/static/") {
		prefixHttp := http.StripPrefix("/static/", http.FileServer(http.Dir("app/static")))
		prefixHttp.ServeHTTP(w, r)
		return true
	}
	return false
}
2.动态路由解析

根据控制器和版本,找到对应的处理器,然后通过反射,调用对应的方法.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
 * @description: 动态路由转发
 * @user: Shershon
 * @param urlPath
 * @return string
 * @return string
 * @date 2021-02-03 15:29:09
 */
func routeForWard(w http.ResponseWriter, r *http.Request) {
	urlPath := r.URL.Path
	if urlPath == "/favicon.ico" {
		return
	}
	// 路由解析
	split := strings.Split(r.URL.Path, "/")
	controller, methodName := split[1], strings.Title(split[2])
	if controller == "" || methodName == "" {
		http.NotFound(w, r)
		return
	}
	// 解析参数
	parseError := r.ParseForm()
	if parseError != nil {
		panic("参数解析失败:" + parseError.Error())
	}
	// 获取版本号
	version := getVersion(r)
	fmt.Println("version:" + version)
	//  匹配路由
	controllerValType := matchControllerObj(version, controller, methodName)
	// 保存请求上下文到控制器基类
	controllerValType.Elem().FieldByName("Response").Set(reflect.ValueOf(w))
	controllerValType.Elem().FieldByName("Request").Set(reflect.ValueOf(r))
	BusErrorInstance.Response = w
	// 调用方法
	controllerValType.MethodByName(methodName).Call(nil)
}

/**
 * @description: 匹配路由
 * @user: Shershon
 * @param version
 * @param controller
 * @param methodName
 * @return interface{}
 * @date 2021-02-19 18:35:07
 */
func matchControllerObj(version, controller, methodName string) reflect.Value {
	fmt.Printf("进入匹配路由: version:%s controller:%s  methodName:%s \n", version, controller, methodName)
	vGroup, ok := RouteListInstance.Route[version]
	if !ok {
		panic(ReqVersionNotExist)
	}
	verNumStr := strings.Trim(version, "ver")
	verNum, _ := strconv.Atoi(verNumStr)
	// 匹配路由
	controllerStruct, ok := vGroup[controller]
  // 当前版本没有,则找下个版本
	if !ok && verNum > 1 {
		newVer := "v" + strconv.Itoa(verNum-1)
		return matchControllerObj(newVer, controller, methodName)
	}
	controllerValType := reflect.ValueOf(controllerStruct)
	// 判断方法是否存在
	valid := controllerValType.MethodByName(methodName).IsValid()
	if !valid {
		panic(ReqMethodNotFoundMsg)
	}
	return controllerValType
}