From 0e786fa4af86343bc2cd31027eafa8ebbf3f3bef Mon Sep 17 00:00:00 2001 From: dan pittman Date: Fri, 5 Aug 2016 16:20:56 -0700 Subject: [PATCH] adds ability to reset params after a filter runs When a filter is run _after_ the router completes, it's input params, such as `":splat"` will have been overwritten by the filter's router pass. This commit adds the ability to tell the router to revert to the previous input params after running a filter. --- context/input.go | 8 ++++++ filter.go | 1 + router.go | 47 +++++++++++++++++++++--------- router_test.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 14 deletions(-) diff --git a/context/input.go b/context/input.go index c47996c9..1683060e 100644 --- a/context/input.go +++ b/context/input.go @@ -301,6 +301,14 @@ func (input *BeegoInput) SetParam(key, val string) { input.pnames = append(input.pnames, key) } +// ResetParams clears any of the input's Params +// This function is used to clear parameters so they may be reset between filter +// passes. +func (input *BeegoInput) ResetParams() { + input.pnames = input.pnames[:0] + input.pvalues = input.pvalues[:0] +} + // Query returns input data item string by a given string. func (input *BeegoInput) Query(key string) string { if val := input.Param(key); val != "" { diff --git a/filter.go b/filter.go index 863223f7..9cc6e913 100644 --- a/filter.go +++ b/filter.go @@ -27,6 +27,7 @@ type FilterRouter struct { tree *Tree pattern string returnOnOutput bool + resetParams bool } // ValidRouter checks if the current request is matched by this filter. diff --git a/router.go b/router.go index d48e509a..6dc94a13 100644 --- a/router.go +++ b/router.go @@ -406,20 +406,27 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) } // InsertFilter Add a FilterFunc with pattern rule and action constant. -// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) +// params is for: +// 1. setting the returnOnOutput value (false allows multiple filters to execute) +// 2. determining whether or not params need to be reset. func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error { - mr := new(FilterRouter) - mr.tree = NewTree() - mr.pattern = pattern - mr.filterFunc = filter - if !BConfig.RouterCaseSensitive { - pattern = strings.ToLower(pattern) + mr := &FilterRouter{ + tree: NewTree(), + pattern: pattern, + filterFunc: filter, + returnOnOutput: true, } - if len(params) == 0 { - mr.returnOnOutput = true - } else { + if !BConfig.RouterCaseSensitive { + mr.pattern = strings.ToLower(pattern) + } + + paramsLen := len(params) + if paramsLen > 0 { mr.returnOnOutput = params[0] } + if paramsLen > 1 { + mr.resetParams = params[1] + } mr.tree.AddRouter(pattern, true) return p.insertFilterRouter(pos, mr) } @@ -581,12 +588,22 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin } func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) { + var preFilterParams map[string]string for _, filterR := range p.filters[pos] { if filterR.returnOnOutput && context.ResponseWriter.Started { return true } + if filterR.resetParams { + preFilterParams = context.Input.Params() + } if ok := filterR.ValidRouter(urlPath, context); ok { filterR.filterFunc(context) + if filterR.resetParams { + context.Input.ResetParams() + for k, v := range preFilterParams { + context.Input.SetParam(k, v) + } + } } if filterR.returnOnOutput && context.ResponseWriter.Started { return true @@ -810,7 +827,9 @@ Admin: var devInfo string statusCode := context.ResponseWriter.Status - if statusCode == 0 { statusCode = 200 } + if statusCode == 0 { + statusCode = 200 + } iswin := (runtime.GOOS == "windows") statusColor := logs.ColorByStatus(iswin, statusCode) @@ -819,9 +838,9 @@ Admin: if findRouter { if routerInfo != nil { - devInfo = fmt.Sprintf("|%s %3d %s|%13s|%8s|%s %s %-7s %-3s r:%s", statusColor, statusCode, - resetColor, timeDur.String(), "match", methodColor, resetColor, r.Method, r.URL.Path, - routerInfo.pattern) + devInfo = fmt.Sprintf("|%s %3d %s|%13s|%8s|%s %s %-7s %-3s r:%s", statusColor, statusCode, + resetColor, timeDur.String(), "match", methodColor, resetColor, r.Method, r.URL.Path, + routerInfo.pattern) } else { devInfo = fmt.Sprintf("|%s %3d %s|%13s|%8s|%s %s %-7s %-3s", statusColor, statusCode, resetColor, timeDur.String(), "match", methodColor, resetColor, r.Method, r.URL.Path) diff --git a/router_test.go b/router_test.go index 9f11286c..936fd5e8 100644 --- a/router_test.go +++ b/router_test.go @@ -420,6 +420,74 @@ func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request return recorder, request } +// Expectation: A Filter with the correct configuration should be created given +// specific parameters. +func TestInsertFilter(t *testing.T) { + testName := "TestInsertFilter" + + mux := NewControllerRegister() + mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}) + if !mux.filters[BeforeRouter][0].returnOnOutput { + t.Errorf( + "%s: passing no variadic params should set returnOnOutput to true", + testName) + } + if mux.filters[BeforeRouter][0].resetParams { + t.Errorf( + "%s: passing no variadic params should set resetParams to false", + testName) + } + + mux = NewControllerRegister() + mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, false) + if mux.filters[BeforeRouter][0].returnOnOutput { + t.Errorf( + "%s: passing false as 1st variadic param should set returnOnOutput to false", + testName) + } + + mux = NewControllerRegister() + mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, true, true) + if !mux.filters[BeforeRouter][0].resetParams { + t.Errorf( + "%s: passing true as 2nd variadic param should set resetParams to true", + testName) + } +} + +// Expectation: the second variadic arg should cause the execution of the filter +// to preserve the parameters from before its execution. +func TestParamResetFilter(t *testing.T) { + testName := "TestParamResetFilter" + route := "/beego/*" // splat + path := "/beego/routes/routes" + + mux := NewControllerRegister() + + mux.InsertFilter("*", BeforeExec, beegoResetParams, true, true) + + mux.Get(route, beegoHandleResetParams) + + rw, r := testRequest("GET", path) + mux.ServeHTTP(rw, r) + + // The two functions, `beegoResetParams` and `beegoHandleResetParams` add + // a response header of `Splat`. The expectation here is that that Header + // value should match what the _request's_ router set, not the filter's. + + headers := rw.HeaderMap + if len(headers["Splat"]) != 1 { + t.Errorf( + "%s: There was an error in the test. Splat param not set in Header", + testName) + } + if headers["Splat"][0] != "routes/routes" { + t.Errorf( + "%s: expected `:splat` param to be [routes/routes] but it was [%s]", + testName, headers["Splat"][0]) + } +} + // Execution point: BeforeRouter // expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle func TestFilterBeforeRouter(t *testing.T) { @@ -612,3 +680,10 @@ func beegoFinishRouter1(ctx *context.Context) { func beegoFinishRouter2(ctx *context.Context) { ctx.WriteString("|FinishRouter2") } +func beegoResetParams(ctx *context.Context) { + ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat")) +} + +func beegoHandleResetParams(ctx *context.Context) { + ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat")) +}