OSDN Git Service

rename (#465)
[bytom/vapor.git] / toolbar / server / handle.go
1 package server
2
3 import (
4         "encoding/json"
5         "net/http"
6         "reflect"
7         "strings"
8
9         "github.com/gin-gonic/gin"
10
11         "github.com/bytom/vapor/errors"
12 )
13
14 func Middleware(serverInstance interface{}) gin.HandlerFunc {
15         return func(c *gin.Context) {
16                 // add Access-Control-Allow-Origin
17                 c.Header("Access-Control-Allow-Origin", "*")
18                 c.Header("Access-Control-Allow-Headers", "Content-Type")
19                 c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
20                 if c.Request.Method == "OPTIONS" {
21                         c.AbortWithStatus(http.StatusOK)
22                         return
23                 }
24
25                 c.Set(serverLabel, serverInstance)
26                 c.Next()
27         }
28 }
29
30 type handlerFun interface{}
31
32 func HandlerMiddleware(handleFunc interface{}) func(*gin.Context) {
33         if err := validateFuncType(handleFunc); err != nil {
34                 panic(err)
35         }
36
37         return func(context *gin.Context) {
38                 handleRequest(context, handleFunc)
39         }
40 }
41
42 func validateFuncType(fun handlerFun) error {
43         ft := reflect.TypeOf(fun)
44         if ft.Kind() != reflect.Func || ft.IsVariadic() {
45                 return errors.New("need nonvariadic func in " + ft.String())
46         }
47
48         if ft.NumIn() < 1 || ft.NumIn() > 3 {
49                 return errors.New("need one or two or three parameters in " + ft.String())
50         }
51
52         if ft.In(0) != contextType {
53                 return errors.New("the first parameter must point of context in " + ft.String())
54         }
55
56         if ft.NumIn() == 2 && ft.In(1).Kind() != reflect.Ptr {
57                 return errors.New("the second parameter must point in " + ft.String())
58         }
59
60         if ft.NumIn() == 3 && ft.In(2) != paginationQueryType {
61                 return errors.New("the third parameter of pagination must point of paginationQuery in " + ft.String())
62         }
63
64         if ft.NumOut() < 1 || ft.NumOut() > 2 {
65                 return errors.New("the size of return value must one or two in " + ft.String())
66         }
67
68         // if has pagination, the first return value must slice or array
69         if ft.NumIn() == 3 && ft.Out(0).Kind() != reflect.Slice && ft.Out(0).Kind() != reflect.Array {
70                 return errors.New("the first return value of pagination must slice of array in " + ft.String())
71         }
72
73         if !ft.Out(ft.NumOut() - 1).Implements(errorType) {
74                 return errors.New("the last return value must error in " + ft.String())
75         }
76         return nil
77 }
78
79 // handleRequest get a handler function to process the request by request url
80 func handleRequest(context *gin.Context, fun handlerFun) {
81         args, err := buildHandleFuncArgs(fun, context)
82         if err != nil {
83                 respondErrorResp(context, err)
84                 return
85         }
86
87         result := callHandleFunc(fun, args...)
88         if err := result[len(result)-1]; err != nil {
89                 respondErrorResp(context, err.(error))
90                 return
91         }
92
93         if exist := processPaginationIfPresent(fun, args, result, context); exist {
94                 return
95         }
96
97         if len(result) == 1 {
98                 respondSuccessResp(context, nil)
99                 return
100         }
101
102         respondSuccessResp(context, result[0])
103 }
104
105 func buildHandleFuncArgs(fun handlerFun, context *gin.Context) ([]interface{}, error) {
106         args := []interface{}{context}
107
108         req, err := createHandleReqArg(fun, context)
109         if err != nil {
110                 return nil, errors.Wrap(err, "createHandleReqArg")
111         }
112
113         if err := checkDisplayOrder(req); err != nil {
114                 return nil, err
115         }
116
117         if req != nil {
118                 args = append(args, req)
119         }
120
121         ft := reflect.TypeOf(fun)
122
123         // no pagination exists
124         if ft.NumIn() != 3 {
125                 return args, nil
126         }
127
128         query, err := parsePagination(context)
129         if err != nil {
130                 return nil, errors.Wrap(err, "ParsePagination")
131         }
132
133         args = append(args, query)
134         return args, nil
135 }
136
137 func createHandleReqArg(fun handlerFun, context *gin.Context) (interface{}, error) {
138         ft := reflect.TypeOf(fun)
139         if ft.NumIn() == 1 {
140                 return nil, nil
141         }
142         argType := ft.In(1).Elem()
143
144         reqArg := reflect.New(argType).Interface()
145         if err := context.ShouldBindJSON(reqArg); err != nil {
146                 return nil, errors.Wrap(err, "bind reqArg")
147         }
148
149         b, err := json.Marshal(reqArg)
150         if err != nil {
151                 return nil, errors.Wrap(err, "json marshal")
152         }
153
154         context.Set(reqBodyLabel, string(b))
155
156         return reqArg, nil
157 }
158
159 func checkDisplayOrder(req interface{}) error {
160         if req == nil {
161                 return nil
162         }
163
164         reqType := reflect.TypeOf(req).Elem()
165         reqVal := reflect.ValueOf(req).Elem()
166
167         for i := 0; i < reqType.NumField(); i++ {
168                 field := reqType.Field(i)
169                 if field.Type != reflect.TypeOf(Display{}) {
170                         continue
171                 }
172                 display := reqVal.Field(i).Interface().(Display)
173
174                 order := strings.Trim(display.Sorter.Order, "")
175                 if order != "desc" && order != "asc" {
176                         reqVal.Field(i).Set(reflect.ValueOf(Display{Filter: display.Filter, Sorter: Sorter{By: display.Sorter.By, Order: "desc"}}))
177                 }
178         }
179         return nil
180 }
181
182 func callHandleFunc(fun handlerFun, args ...interface{}) []interface{} {
183         fv := reflect.ValueOf(fun)
184
185         params := make([]reflect.Value, len(args))
186         for i, arg := range args {
187                 params[i] = reflect.ValueOf(arg)
188         }
189
190         rs := fv.Call(params)
191         result := make([]interface{}, len(rs))
192         for i, r := range rs {
193                 result[i] = r.Interface()
194         }
195         return result
196 }