github.com/lingyao2333/mo-zero@v1.4.1/rest/chain/chain.go (about) 1 package chain 2 3 // This is a modified version of https://github.com/justinas/alice 4 // The original code is licensed under the MIT license. 5 // It's modified for couple reasons: 6 // - Added the Chain interface 7 // - Added support for the Chain.Prepend(...) method 8 9 import "net/http" 10 11 type ( 12 // Chain defines a chain of middleware. 13 Chain interface { 14 Append(middlewares ...Middleware) Chain 15 Prepend(middlewares ...Middleware) Chain 16 Then(h http.Handler) http.Handler 17 ThenFunc(fn http.HandlerFunc) http.Handler 18 } 19 20 // Middleware is an HTTP middleware. 21 Middleware func(http.Handler) http.Handler 22 23 // chain acts as a list of http.Handler middlewares. 24 // chain is effectively immutable: 25 // once created, it will always hold 26 // the same set of middlewares in the same order. 27 chain struct { 28 middlewares []Middleware 29 } 30 ) 31 32 // New creates a new Chain, memorizing the given list of middleware middlewares. 33 // New serves no other function, middlewares are only called upon a call to Then() or ThenFunc(). 34 func New(middlewares ...Middleware) Chain { 35 return chain{middlewares: append(([]Middleware)(nil), middlewares...)} 36 } 37 38 // Append extends a chain, adding the specified middlewares as the last ones in the request flow. 39 // 40 // c := chain.New(m1, m2) 41 // c.Append(m3, m4) 42 // // requests in c go m1 -> m2 -> m3 -> m4 43 func (c chain) Append(middlewares ...Middleware) Chain { 44 return chain{middlewares: join(c.middlewares, middlewares)} 45 } 46 47 // Prepend extends a chain by adding the specified chain as the first one in the request flow. 48 // 49 // c := chain.New(m3, m4) 50 // c1 := chain.New(m1, m2) 51 // c.Prepend(c1) 52 // // requests in c go m1 -> m2 -> m3 -> m4 53 func (c chain) Prepend(middlewares ...Middleware) Chain { 54 return chain{middlewares: join(middlewares, c.middlewares)} 55 } 56 57 // Then chains the middleware and returns the final http.Handler. 58 // New(m1, m2, m3).Then(h) 59 // is equivalent to: 60 // m1(m2(m3(h))) 61 // When the request comes in, it will be passed to m1, then m2, then m3 62 // and finally, the given handler 63 // (assuming every middleware calls the following one). 64 // 65 // A chain can be safely reused by calling Then() several times. 66 // stdStack := chain.New(ratelimitHandler, csrfHandler) 67 // indexPipe = stdStack.Then(indexHandler) 68 // authPipe = stdStack.Then(authHandler) 69 // Note that middlewares are called on every call to Then() or ThenFunc() 70 // and thus several instances of the same middleware will be created 71 // when a chain is reused in this way. 72 // For proper middleware, this should cause no problems. 73 // 74 // Then() treats nil as http.DefaultServeMux. 75 func (c chain) Then(h http.Handler) http.Handler { 76 if h == nil { 77 h = http.DefaultServeMux 78 } 79 80 for i := range c.middlewares { 81 h = c.middlewares[len(c.middlewares)-1-i](h) 82 } 83 84 return h 85 } 86 87 // ThenFunc works identically to Then, but takes 88 // a HandlerFunc instead of a Handler. 89 // 90 // The following two statements are equivalent: 91 // c.Then(http.HandlerFunc(fn)) 92 // c.ThenFunc(fn) 93 // 94 // ThenFunc provides all the guarantees of Then. 95 func (c chain) ThenFunc(fn http.HandlerFunc) http.Handler { 96 // This nil check cannot be removed due to the "nil is not nil" common mistake in Go. 97 // Required due to: https://stackoverflow.com/questions/33426977/how-to-golang-check-a-variable-is-nil 98 if fn == nil { 99 return c.Then(nil) 100 } 101 return c.Then(fn) 102 } 103 104 func join(a, b []Middleware) []Middleware { 105 mids := make([]Middleware, 0, len(a)+len(b)) 106 mids = append(mids, a...) 107 mids = append(mids, b...) 108 return mids 109 }