github.com/lenfree/buffalo@v0.7.3-0.20170207163156-891616ea4064/middleware.go (about) 1 package buffalo 2 3 import ( 4 "reflect" 5 "runtime" 6 "strings" 7 ) 8 9 // MiddlewareFunc defines the interface for a piece of Buffalo 10 // Middleware. 11 /* 12 func DoSomething(next Handler) Handler { 13 return func(c Context) error { 14 // do something before calling the next handler 15 err := next(c) 16 // do something after call the handler 17 return err 18 } 19 } 20 */ 21 type MiddlewareFunc func(Handler) Handler 22 23 // Use the specified Middleware for the App. 24 // When defined on an `*App` the specified middleware will be 25 // inherited by any `Group` calls that are made on that on 26 // the App. 27 func (a *App) Use(mw ...MiddlewareFunc) { 28 a.Middleware.Use(mw...) 29 } 30 31 // MiddlewareStack manages the middleware stack for an App/Group. 32 type MiddlewareStack struct { 33 stack []MiddlewareFunc 34 skips map[string]bool 35 } 36 37 func (ms *MiddlewareStack) clone() *MiddlewareStack { 38 n := newMiddlewareStack() 39 for _, s := range ms.stack { 40 n.stack = append(n.stack, s) 41 } 42 for k, v := range ms.skips { 43 n.skips[k] = v 44 } 45 return n 46 } 47 48 // Clear wipes out the current middleware stack for the App/Group, 49 // any middleware previously defined will be removed leaving an empty 50 // middleware stack. 51 func (ms *MiddlewareStack) Clear() { 52 ms.stack = []MiddlewareFunc{} 53 ms.skips = map[string]bool{} 54 } 55 56 // Use the specified Middleware for the App. 57 // When defined on an `*App` the specified middleware will be 58 // inherited by any `Group` calls that are made on that on 59 // the App. 60 func (ms *MiddlewareStack) Use(mw ...MiddlewareFunc) { 61 ms.stack = append(ms.stack, mw...) 62 } 63 64 // Skip a specified piece of middleware the specified Handlers. 65 // This is useful for things like wrapping your application in an 66 // authorization middleare, but skipping it for things the home 67 // page, the login page, etc... 68 /* 69 a.Middleware.Skip(Authorization, HomeHandler, LoginHandler, RegistrationHandler) 70 */ 71 // NOTE: When skipping Resource handlers, you need to first declare your 72 // resource handler as a type of buffalo.Resource for the Skip function to 73 // properly recognize and match it. 74 /* 75 // Works: 76 var ur Resource 77 cr = &carsResource{&buffaloBaseResource{}} 78 g = a.Resource("/cars", cr) 79 g.Use(SomeMiddleware) 80 g.Middleware.Skip(SomeMiddleware, cr.Show) 81 82 // Doesn't Work: 83 cr := &carsResource{&buffaloBaseResource{}} 84 g = a.Resource("/cars", cr) 85 g.Use(SomeMiddleware) 86 g.Middleware.Skip(SomeMiddleware, cr.Show) 87 */ 88 func (ms *MiddlewareStack) Skip(mw MiddlewareFunc, handlers ...Handler) { 89 for _, h := range handlers { 90 key := funcKey(mw, h) 91 ms.skips[key] = true 92 } 93 } 94 95 // Replace a piece of middleware with another piece of middleware. Great for 96 // testing. 97 func (ms *MiddlewareStack) Replace(mw1 MiddlewareFunc, mw2 MiddlewareFunc) { 98 m1k := funcKey(mw1) 99 stack := []MiddlewareFunc{} 100 for _, mw := range ms.stack { 101 if funcKey(mw) == m1k { 102 stack = append(stack, mw2) 103 } else { 104 stack = append(stack, mw) 105 } 106 } 107 ms.stack = stack 108 } 109 110 func (ms *MiddlewareStack) handler(h Handler) Handler { 111 if len(ms.stack) > 0 { 112 mh := func(_ Handler) Handler { 113 return h 114 } 115 116 tstack := []MiddlewareFunc{mh} 117 118 sl := len(ms.stack) - 1 119 for i := sl; i >= 0; i-- { 120 mw := ms.stack[i] 121 key := funcKey(mw, h) 122 if !ms.skips[key] { 123 tstack = append(tstack, mw) 124 } 125 } 126 127 for _, mw := range tstack { 128 h = mw(h) 129 } 130 return h 131 } 132 return h 133 } 134 135 func newMiddlewareStack(mws ...MiddlewareFunc) *MiddlewareStack { 136 return &MiddlewareStack{ 137 stack: mws, 138 skips: map[string]bool{}, 139 } 140 } 141 142 func funcKey(funcs ...interface{}) string { 143 names := []string{} 144 for _, f := range funcs { 145 rv := reflect.ValueOf(f) 146 ptr := rv.Pointer() 147 if n, ok := keyMap[rv.Pointer()]; ok { 148 names = append(names, n) 149 // fmt.Printf("### found %+v -> %+v\n", ptr, n) 150 continue 151 } 152 fnc := runtime.FuncForPC(ptr) 153 n := fnc.Name() 154 // fmt.Printf("### not found %+v -> %+v\n", ptr, n) 155 keyMap[ptr] = n 156 names = append(names, n) 157 } 158 return strings.Join(names, "/") 159 } 160 161 var keyMap = map[uintptr]string{}