github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/net/http/httpjson/handler.go (about) 1 package httpjson 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "net/http" 8 "reflect" 9 ) 10 11 // ErrorWriter is responsible for writing the provided error value 12 // to the response. 13 type ErrorWriter func(context.Context, http.ResponseWriter, error) 14 15 // DefaultResponse will be sent as the response body 16 // when the handler function signature 17 // has no return value. 18 var DefaultResponse = json.RawMessage(`{"message":"ok"}`) 19 20 // handler is an http.Handler that calls a function for each request. 21 // It uses the signature of the function to decide how to interpret 22 type handler struct { 23 fv reflect.Value 24 inType reflect.Type 25 hasCtx bool 26 errFunc ErrorWriter 27 } 28 29 // Handler returns an HTTP handler for function f. 30 // See the package doc for details on allowed signatures for f. 31 // If f returns a non-nil error, the handler will call errFunc. 32 func Handler(f interface{}, errFunc ErrorWriter) (http.Handler, error) { 33 fv := reflect.ValueOf(f) 34 hasCtx, inType, err := funcInputType(fv) 35 if err != nil { 36 return nil, err 37 } 38 39 h := &handler{fv, inType, hasCtx, errFunc} 40 return h, nil 41 } 42 43 func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 44 var a []reflect.Value 45 if h.hasCtx { 46 ctx := req.Context() 47 ctx = context.WithValue(ctx, reqKey, req) 48 ctx = context.WithValue(ctx, respKey, w) 49 a = append(a, reflect.ValueOf(ctx)) 50 } 51 if h.inType != nil { 52 inPtr := reflect.New(h.inType) 53 err := Read(req.Body, inPtr.Interface()) 54 if err != nil { 55 h.errFunc(req.Context(), w, err) 56 return 57 } 58 a = append(a, inPtr.Elem()) 59 } 60 rv := h.fv.Call(a) 61 62 var ( 63 res interface{} 64 err error 65 ) 66 switch n := len(rv); { 67 case n == 0: 68 res = &DefaultResponse 69 case n == 1 && !h.fv.Type().Out(0).Implements(errorType): 70 res = rv[0].Interface() 71 case n == 1 && h.fv.Type().Out(0).Implements(errorType): 72 // out param is of type error; its value can still be nil 73 res = &DefaultResponse 74 err, _ = rv[0].Interface().(error) 75 case n == 2: 76 res = rv[0].Interface() 77 err, _ = rv[1].Interface().(error) 78 } 79 if err != nil { 80 h.errFunc(req.Context(), w, err) 81 return 82 } 83 84 Write(req.Context(), w, 200, res) 85 } 86 87 var ( 88 errorType = reflect.TypeOf((*error)(nil)).Elem() 89 contextType = reflect.TypeOf((*context.Context)(nil)).Elem() 90 ) 91 92 func funcInputType(fv reflect.Value) (hasCtx bool, t reflect.Type, err error) { 93 ft := fv.Type() 94 if ft.Kind() != reflect.Func || ft.IsVariadic() { 95 return false, nil, errors.New("need nonvariadic func in " + ft.String()) 96 } 97 98 off := 0 // or 1 with context 99 hasCtx = ft.NumIn() >= 1 && ft.In(0).Implements(contextType) 100 if hasCtx { 101 off = 1 102 } 103 104 if ft.NumIn() > off+1 { 105 return false, nil, errors.New("too many params in " + ft.String()) 106 } 107 108 if ft.NumIn() == off+1 { 109 t = ft.In(ft.NumIn() - 1) 110 } 111 112 if n := ft.NumOut(); n == 2 && !ft.Out(1).Implements(errorType) { 113 return false, nil, errors.New("second return value must be error in " + ft.String()) 114 } else if n > 2 { 115 return false, nil, errors.New("need at most two return values in " + ft.String()) 116 } 117 118 return hasCtx, t, nil 119 }