1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
12 // Map from "Controller" or "Controller.Method" to the Filter chain
13 var filterOverrides = make(map[string][]Filter)
15 // FilterConfigurator allows the developer configure the filter chain on a
16 // per-controller or per-action basis. The filter configuration is applied by
17 // the FilterConfiguringFilter, which is itself a filter stage. For example,
20 // Filters = []Filter{
22 // FilterConfiguringFilter,
28 // FilterAction(App.Action).
31 // => RouterFilter, FilterConfiguringFilter, SessionFilter, OtherFilter, ActionInvoker
34 // FilterAction(App.Action).
35 // Remove(SessionFilter)
37 // => RouterFilter, FilterConfiguringFilter, OtherFilter, ActionInvoker
40 // FilterAction(App.Action).
41 // Insert(OtherFilter, revel.BEFORE, SessionFilter)
43 // => RouterFilter, FilterConfiguringFilter, OtherFilter, SessionFilter, ActionInvoker
45 // Filter modifications may be combined between Controller and Action. For example:
46 // FilterController(App{}).
48 // FilterAction(App.Action).
51 // .. would result in App.Action being filtered by both Filter1 and Filter2.
53 // Note: the last filter stage is not subject to the configurator. In
54 // particular, Add() adds a filter to the second-to-last place.
55 type FilterConfigurator struct {
56 key string // e.g. "App", "App.Action"
57 controllerName string // e.g. "App"
60 func newFilterConfigurator(controllerName, methodName string) FilterConfigurator {
62 return FilterConfigurator{controllerName, controllerName}
64 return FilterConfigurator{controllerName + "." + methodName, controllerName}
67 // FilterController returns a configurator for the filters applied to all
68 // actions on the given controller instance. For example:
69 // FilterController(MyController{})
70 func FilterController(controllerInstance interface{}) FilterConfigurator {
71 t := reflect.TypeOf(controllerInstance)
72 for t.Kind() == reflect.Ptr {
75 return newFilterConfigurator(t.Name(), "")
78 // FilterAction returns a configurator for the filters applied to the given
79 // controller method. For example:
80 // FilterAction(MyController.MyAction)
81 func FilterAction(methodRef interface{}) FilterConfigurator {
83 methodValue = reflect.ValueOf(methodRef)
84 methodType = methodValue.Type()
86 if methodType.Kind() != reflect.Func || methodType.NumIn() == 0 {
87 panic("Expecting a controller method reference (e.g. Controller.Action), got a " +
91 controllerType := methodType.In(0)
92 method := FindMethod(controllerType, methodValue)
94 panic("Action not found on controller " + controllerType.Name())
97 for controllerType.Kind() == reflect.Ptr {
98 controllerType = controllerType.Elem()
101 return newFilterConfigurator(controllerType.Name(), method.Name)
104 // Add the given filter in the second-to-last position in the filter chain.
105 // (Second-to-last so that it is before ActionInvoker)
106 func (conf FilterConfigurator) Add(f Filter) FilterConfigurator {
107 conf.apply(func(fc []Filter) []Filter {
108 return conf.addFilter(f, fc)
113 func (conf FilterConfigurator) addFilter(f Filter, fc []Filter) []Filter {
114 return append(fc[:len(fc)-1], f, fc[len(fc)-1])
117 // Remove a filter from the filter chain.
118 func (conf FilterConfigurator) Remove(target Filter) FilterConfigurator {
119 conf.apply(func(fc []Filter) []Filter {
120 return conf.rmFilter(target, fc)
125 func (conf FilterConfigurator) rmFilter(target Filter, fc []Filter) []Filter {
126 for i, f := range fc {
127 if FilterEq(f, target) {
128 return append(fc[:i], fc[i+1:]...)
134 // Insert a filter into the filter chain before or after another.
135 // This may be called with the BEFORE or AFTER constants, for example:
136 // revel.FilterAction(App.Index).
137 // Insert(MyFilter, revel.BEFORE, revel.ActionInvoker).
138 // Insert(MyFilter2, revel.AFTER, revel.PanicFilter)
139 func (conf FilterConfigurator) Insert(insert Filter, where When, target Filter) FilterConfigurator {
140 if where != BEFORE && where != AFTER {
141 panic("where must be BEFORE or AFTER")
143 conf.apply(func(fc []Filter) []Filter {
144 return conf.insertFilter(insert, where, target, fc)
149 func (conf FilterConfigurator) insertFilter(insert Filter, where When, target Filter, fc []Filter) []Filter {
150 for i, f := range fc {
151 if FilterEq(f, target) {
153 return append(fc[:i], append([]Filter{insert}, fc[i:]...)...)
155 return append(fc[:i+1], append([]Filter{insert}, fc[i+1:]...)...)
161 // getChain returns the filter chain that applies to the given controller or
162 // action. If no overrides are configured, then a copy of the default filter
163 // chain is returned.
164 func (conf FilterConfigurator) getChain() []Filter {
166 if filters = getOverrideChain(conf.controllerName, conf.key); filters == nil {
167 // The override starts with all filters after FilterConfiguringFilter
168 for i, f := range Filters {
169 if FilterEq(f, FilterConfiguringFilter) {
170 filters = make([]Filter, len(Filters)-i-1)
171 copy(filters, Filters[i+1:])
176 panic("FilterConfiguringFilter not found in revel.Filters.")
182 // apply applies the given functional change to the filter overrides.
183 // No other function modifies the filterOverrides map.
184 func (conf FilterConfigurator) apply(f func([]Filter) []Filter) {
185 // Updates any actions that have had their filters overridden, if this is a
186 // Controller configurator.
187 if conf.controllerName == conf.key {
188 for k, v := range filterOverrides {
189 if strings.HasPrefix(k, conf.controllerName+".") {
190 filterOverrides[k] = f(v)
195 // Update the Controller or Action overrides.
196 filterOverrides[conf.key] = f(conf.getChain())
199 // FilterEq returns true if the two filters reference the same filter.
200 func FilterEq(a, b Filter) bool {
201 return reflect.ValueOf(a).Pointer() == reflect.ValueOf(b).Pointer()
204 // FilterConfiguringFilter is a filter stage that customizes the remaining
205 // filter chain for the action being invoked.
206 func FilterConfiguringFilter(c *Controller, fc []Filter) {
207 if newChain := getOverrideChain(c.Name, c.Action); newChain != nil {
208 newChain[0](c, newChain[1:])
214 // getOverrideChain retrieves the overrides for the action that is set
215 func getOverrideChain(controllerName, action string) []Filter {
216 if newChain, ok := filterOverrides[action]; ok {
219 if newChain, ok := filterOverrides[controllerName]; ok {