1package render 2 3import ( 4 "net/http" 5 "reflect" 6) 7 8// Renderer interface for managing response payloads. 9type Renderer interface { 10 Render(w http.ResponseWriter, r *http.Request) error 11} 12 13// Binder interface for managing request payloads. 14type Binder interface { 15 Bind(r *http.Request) error 16} 17 18// Bind decodes a request body and executes the Binder method of the 19// payload structure. 20func Bind(r *http.Request, v Binder) error { 21 if err := Decode(r, v); err != nil { 22 return err 23 } 24 return binder(r, v) 25} 26 27// Render renders a single payload and respond to the client request. 28func Render(w http.ResponseWriter, r *http.Request, v Renderer) error { 29 if err := renderer(w, r, v); err != nil { 30 return err 31 } 32 Respond(w, r, v) 33 return nil 34} 35 36// RenderList renders a slice of payloads and responds to the client request. 37func RenderList(w http.ResponseWriter, r *http.Request, l []Renderer) error { 38 for _, v := range l { 39 if err := renderer(w, r, v); err != nil { 40 return err 41 } 42 } 43 Respond(w, r, l) 44 return nil 45} 46 47func isNil(f reflect.Value) bool { 48 switch f.Kind() { 49 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 50 return f.IsNil() 51 default: 52 return false 53 } 54} 55 56// Executed top-down 57func renderer(w http.ResponseWriter, r *http.Request, v Renderer) error { 58 rv := reflect.ValueOf(v) 59 if rv.Kind() == reflect.Ptr { 60 rv = rv.Elem() 61 } 62 63 // We call it top-down. 64 if err := v.Render(w, r); err != nil { 65 return err 66 } 67 68 // We're done if the Renderer isn't a struct object 69 if rv.Kind() != reflect.Struct { 70 return nil 71 } 72 73 // For structs, we call Render on each field that implements Renderer 74 for i := 0; i < rv.NumField(); i++ { 75 f := rv.Field(i) 76 if f.Type().Implements(rendererType) { 77 78 if isNil(f) { 79 continue 80 } 81 82 fv := f.Interface().(Renderer) 83 if err := renderer(w, r, fv); err != nil { 84 return err 85 } 86 87 } 88 } 89 90 return nil 91} 92 93// Executed bottom-up 94func binder(r *http.Request, v Binder) error { 95 rv := reflect.ValueOf(v) 96 if rv.Kind() == reflect.Ptr { 97 rv = rv.Elem() 98 } 99 100 // Call Binder on non-struct types right away 101 if rv.Kind() != reflect.Struct { 102 return v.Bind(r) 103 } 104 105 // For structs, we call Bind on each field that implements Binder 106 for i := 0; i < rv.NumField(); i++ { 107 f := rv.Field(i) 108 if f.Type().Implements(binderType) { 109 110 if isNil(f) { 111 continue 112 } 113 114 fv := f.Interface().(Binder) 115 if err := binder(r, fv); err != nil { 116 return err 117 } 118 } 119 } 120 121 // We call it bottom-up 122 if err := v.Bind(r); err != nil { 123 return err 124 } 125 126 return nil 127} 128 129var ( 130 rendererType = reflect.TypeOf(new(Renderer)).Elem() 131 binderType = reflect.TypeOf(new(Binder)).Elem() 132) 133 134// contextKey is a value for use with context.WithValue. It's used as 135// a pointer so it fits in an interface{} without allocation. This technique 136// for defining context keys was copied from Go 1.7's new use of context in net/http. 137type contextKey struct { 138 name string 139} 140 141func (k *contextKey) String() string { 142 return "chi render context value " + k.name 143} 144