forked from andeya/lessgo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapihandler.go
200 lines (179 loc) · 5.53 KB
/
apihandler.go
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package lessgo
import (
"reflect"
"runtime"
"sort"
"strings"
"sync"
"github.com/lessgo/lessgo/utils"
)
type (
ApiHandler struct {
Desc string // (可选)本操作的描述
Method string // (必填)请求方法,"*"表示除"WS"外全部方法,多方法写法:"GET|POST"或"GET POST",冲突时优先级WS>GET>*
Params []Param // (必填)参数说明列表(应该只声明当前中间件用到的参数),path参数类型的先后顺序与url中保持一致
HTTP200 []Result // (可选)HTTP Status Code 为200时的响应结果
Handler func(*Context) error // (必填)操作
id string // 操作的唯一标识符
methods []string // 真实的请求方法列表
suffix string // 路由节点的url参数后缀
inited bool // 标记是否已经初始化过
lock sync.Mutex
}
Param struct {
Name string // (必填)参数名
In string // (必填)参数出现位置
Required bool // (必填)是否必填
Model interface{} // (必填)参数值,API文档中依此推断参数值类型,同时作为默认值,当为nil时表示file文件上传
Desc string // (可选)参数描述
}
Result struct {
Code int `json:"code"` // (必填)返回结果码
Info interface{} `json:"info,omitempty"` // (可选)返回结果格式参考或描述
// Header interface{} // (可选)返回结果头部信息
}
)
/*
* 关于参数的说明
* 一、数据结构主要用于固定格式的服务器响应结构,适用于多个接口可能返回相同的数据结构,编辑保存后相关所有的引用都会变更。
* 支持的数据类型说明如下:
* 1、string:字符串类型
* 2、array:数组类型,子项只能是支持的数据类型中的一种,不能添加多个
* 3、object:对象类型,只支持一级属性,不支持嵌套,嵌套可以通过在属性中引入ref类型的对象或自定义数据格式
* 4、int:短整型
* 5、long:长整型
* 6、float:浮点型
* 7、double:浮点型
* 8、decimal:精确到比较高的浮点型
* 9、ref:引用类型,即引用定义好的数据结构
* 10、file:文件(Param.Model==nil)
*
* 二、参数位置
* body:http请求body
* cookie:本地cookie
* formData:表单参数
* header:http请求header
* path:http请求url,如getInfo/{userId}
* query:http请求拼接,如getInfo?userId={userId}
* 三、参数类型
* 自定义:目前仅支持自定义json格式,仅当"参数位置"为“body"有效
*/
var (
apiHandlerMap = map[string]*ApiHandler{}
apiHandlerLock sync.RWMutex
)
func NilApiHandler(desc string) *ApiHandler {
a := &ApiHandler{
Desc: desc,
}
a.initId()
a.inited = true
if getApiHandler(a.id) != nil {
return apiHandlerMap[a.id]
}
apiHandlerLock.Lock()
defer apiHandlerLock.Unlock()
apiHandlerMap[a.id] = a
return a
}
// 注册操作
func (a ApiHandler) Reg() *ApiHandler {
return a.init()
}
// 初始化并保存在全局唯一的操作列表中
func (a *ApiHandler) init() *ApiHandler {
a.lock.Lock()
defer a.lock.Unlock()
if a.inited {
return getApiHandler(a.id)
}
a.initMethod()
a.initParamsAndSuffix()
a.initId()
a.inited = true
if h := getApiHandler(a.id); h != nil {
return h
}
setApiHandler(a)
return a
}
// 虚拟操作的id
func (a *ApiHandler) Id() string {
return a.id
}
// // 操作的url前缀
// func (a *ApiHandler) Suffix() string {
// return a.suffix
// }
// 真实的请求方法列表(自动转换: "WS"->"GET", "*"->methods)
func (a *ApiHandler) Methods() []string {
return a.methods
}
func getApiHandler(id string) *ApiHandler {
apiHandlerLock.RLock()
defer apiHandlerLock.RUnlock()
return apiHandlerMap[id]
}
func setApiHandler(vh *ApiHandler) {
apiHandlerLock.Lock()
defer apiHandlerLock.Unlock()
apiHandlerMap[vh.id] = vh
for i, vh2 := range lessgo.apiHandlers {
if vh.Id() < vh2.Id() {
list := make([]*ApiHandler, len(lessgo.apiHandlers)+1)
copy(list, lessgo.apiHandlers[:i])
list[i] = vh
copy(list[i+1:], lessgo.apiHandlers[i:])
lessgo.apiHandlers = list
return
}
}
lessgo.apiHandlers = append(lessgo.apiHandlers, vh)
}
func (a *ApiHandler) initParamsAndSuffix() {
a.suffix = ""
for i, count := 0, len(a.Params); i < count; i++ {
if a.Params[i].In == "path" {
a.Params[i].Required = true //path参数不可缺省
a.suffix += "/:" + a.Params[i].Name
}
}
}
func (a *ApiHandler) initMethod() {
defer func() {
sort.Strings(a.methods) //方法排序,保证一致性
a.Method = strings.Join(a.methods, "|") //格式化,保证一致性
}()
a.methods = []string{}
a.Method = strings.ToUpper(a.Method)
// 检查websocket方法,若存在则不允许GET方法存在
if strings.Contains(a.Method, WS) {
a.Method = strings.Replace(a.Method, GET, "", -1)
a.methods = append(a.methods, WS)
}
// 遍历标准方法
for _, method := range methods {
if strings.Contains(a.Method, method) {
a.methods = append(a.methods, method)
}
}
// 当只含有 * 时表示除WS外任意方法
if len(a.methods) == 0 {
if strings.Contains(a.Method, ANY) {
a.methods = methods[:]
} else {
Log.Fatal("ApiHandler \"%v\"'s method can't be %v. ", a.Desc, a.Method)
}
}
}
func (a *ApiHandler) initId() {
add := "[" + a.suffix + "]" + "[" + a.Desc + "]" + "[" + a.Method + "]"
v := reflect.ValueOf(a.Handler)
t := v.Type()
if t.Kind() == reflect.Func {
a.id = runtime.FuncForPC(v.Pointer()).Name() + add
} else {
a.id = t.String() + add
}
a.id = utils.MakeHash(a.id)
}