diff --git a/adapter/app.go b/adapter/app.go index 8502256b..565a9795 100644 --- a/adapter/app.go +++ b/adapter/app.go @@ -74,7 +74,7 @@ func oldMiddlewareToNew(mws []MiddleWare) []web.MiddleWare { // beego.Router("/api/update",&RestController{},"put:UpdateFood") // beego.Router("/api/delete",&RestController{},"delete:DeleteFood") func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { - return (*App)(web.Router(rootpath, c, mappingMethods...)) + return (*App)(web.Router(rootpath, c, web.SetRouterMethods(c, mappingMethods...))) } // UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful diff --git a/adapter/router.go b/adapter/router.go index 900e3eb7..17e270ca 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -87,7 +87,7 @@ func NewControllerRegister() *ControllerRegister { // Add("/api",&RestController{},"get,post:ApiFunc" // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { - (*web.ControllerRegister)(p).Add(pattern, c, mappingMethods...) + (*web.ControllerRegister)(p).Add(pattern, c, web.SetRouterMethods(c, mappingMethods...)) } // Include only when the Runmode is dev will generate router file in the router/auto.go from the controller diff --git a/client/httplib/http_response_test.go b/client/httplib/http_response_test.go new file mode 100644 index 00000000..90db3fca --- /dev/null +++ b/client/httplib/http_response_test.go @@ -0,0 +1,36 @@ +// Copyright 2020 beego +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httplib + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + + +func TestNewHttpResponseWithJsonBody(t *testing.T) { + // string + resp := NewHttpResponseWithJsonBody("{}") + assert.Equal(t, int64(2), resp.ContentLength) + + resp = NewHttpResponseWithJsonBody([]byte("{}")) + assert.Equal(t, int64(2), resp.ContentLength) + + resp = NewHttpResponseWithJsonBody(&user{ + Name: "Tom", + }) + assert.True(t, resp.ContentLength > 0) +} diff --git a/client/httplib/httplib.go b/client/httplib/httplib.go index 9402eca6..f89c6fa2 100644 --- a/client/httplib/httplib.go +++ b/client/httplib/httplib.go @@ -44,49 +44,22 @@ import ( "mime/multipart" "net" "net/http" - "net/http/cookiejar" "net/http/httputil" "net/url" "os" "path" "strings" - "sync" "time" "gopkg.in/yaml.v2" ) -var defaultSetting = BeegoHTTPSettings{ - UserAgent: "beegoServer", - ConnectTimeout: 60 * time.Second, - ReadWriteTimeout: 60 * time.Second, - Gzip: true, - DumpBody: true, - FilterChains: []FilterChain{mockFilter.FilterChain}, -} - -var defaultCookieJar http.CookieJar -var settingMutex sync.Mutex // it will be the last filter and execute request.Do var doRequestFilter = func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) { return req.doRequest(ctx) } -// createDefaultCookie creates a global cookiejar to store cookies. -func createDefaultCookie() { - settingMutex.Lock() - defer settingMutex.Unlock() - defaultCookieJar, _ = cookiejar.New(nil) -} - -// SetDefaultSetting overwrites default settings -func SetDefaultSetting(setting BeegoHTTPSettings) { - settingMutex.Lock() - defer settingMutex.Unlock() - defaultSetting = setting -} - // NewBeegoRequest returns *BeegoHttpRequest with specific method func NewBeegoRequest(rawurl, method string) *BeegoHTTPRequest { var resp http.Response @@ -137,23 +110,7 @@ func Head(url string) *BeegoHTTPRequest { return NewBeegoRequest(url, "HEAD") } -// BeegoHTTPSettings is the http.Client setting -type BeegoHTTPSettings struct { - ShowDebug bool - UserAgent string - ConnectTimeout time.Duration - ReadWriteTimeout time.Duration - TLSClientConfig *tls.Config - Proxy func(*http.Request) (*url.URL, error) - Transport http.RoundTripper - CheckRedirect func(req *http.Request, via []*http.Request) error - EnableCookie bool - Gzip bool - DumpBody bool - Retries int // if set to -1 means will retry forever - RetryDelay time.Duration - FilterChains []FilterChain -} + // BeegoHTTPRequest provides more useful methods than http.Request for requesting a url. type BeegoHTTPRequest struct { @@ -362,6 +319,9 @@ func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) { return b, err } b.req.Body = ioutil.NopCloser(bytes.NewReader(byts)) + b.req.GetBody = func() (io.ReadCloser, error) { + return ioutil.NopCloser(bytes.NewReader(byts)), nil + } b.req.ContentLength = int64(len(byts)) b.req.Header.Set("Content-Type", "application/xml") } diff --git a/client/httplib/httplib_test.go b/client/httplib/httplib_test.go index d0f826cb..1763b1b5 100644 --- a/client/httplib/httplib_test.go +++ b/client/httplib/httplib_test.go @@ -300,3 +300,81 @@ func TestAddFilter(t *testing.T) { r := Get("http://beego.me") assert.Equal(t, 1, len(req.setting.FilterChains)-len(r.setting.FilterChains)) } + +func TestHead(t *testing.T) { + req := Head("http://beego.me") + assert.NotNil(t, req) + assert.Equal(t, "HEAD", req.req.Method) +} + +func TestDelete(t *testing.T) { + req := Delete("http://beego.me") + assert.NotNil(t, req) + assert.Equal(t, "DELETE", req.req.Method) +} + +func TestPost(t *testing.T) { + req := Post("http://beego.me") + assert.NotNil(t, req) + assert.Equal(t, "POST", req.req.Method) +} + +func TestNewBeegoRequest(t *testing.T) { + req := NewBeegoRequest("http://beego.me", "GET") + assert.NotNil(t, req) + assert.Equal(t, "GET", req.req.Method) +} + +func TestPut(t *testing.T) { + req := Put("http://beego.me") + assert.NotNil(t, req) + assert.Equal(t, "PUT", req.req.Method) +} + +func TestBeegoHTTPRequest_Header(t *testing.T) { + req := Post("http://beego.me") + key, value := "test-header", "test-header-value" + req.Header(key, value) + assert.Equal(t, value, req.req.Header.Get(key)) +} + +func TestBeegoHTTPRequest_SetHost(t *testing.T) { + req := Post("http://beego.me") + host := "test-hose" + req.SetHost(host) + assert.Equal(t, host, req.req.Host) +} + +func TestBeegoHTTPRequest_Param(t *testing.T) { + req := Post("http://beego.me") + key, value := "test-param", "test-param-value" + req.Param(key, value) + assert.Equal(t, value, req.params[key][0]) + + value1 := "test-param-value-1" + req.Param(key, value1) + assert.Equal(t, value1, req.params[key][1]) +} + +func TestBeegoHTTPRequest_Body(t *testing.T) { + req := Post("http://beego.me") + body := `hello, world` + req.Body([]byte(body)) + assert.Equal(t, int64(len(body)), req.req.ContentLength) + assert.NotNil(t, req.req.GetBody) +} + + +type user struct { + Name string `xml:"name"` +} +func TestBeegoHTTPRequest_XMLBody(t *testing.T) { + req := Post("http://beego.me") + body := &user{ + Name: "Tom", + } + _, err := req.XMLBody(body) + assert.True(t, req.req.ContentLength > 0) + assert.Nil(t, err) + assert.NotNil(t, req.req.GetBody) +} \ No newline at end of file diff --git a/client/httplib/mock.go b/client/httplib/mock/mock.go similarity index 90% rename from client/httplib/mock.go rename to client/httplib/mock/mock.go index 691f03d2..7640e454 100644 --- a/client/httplib/mock.go +++ b/client/httplib/mock/mock.go @@ -12,17 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package httplib +package mock import ( "context" "net/http" + "github.com/beego/beego/v2/client/httplib" "github.com/beego/beego/v2/core/logs" ) const mockCtxKey = "beego-httplib-mock" +func init() { + InitMockSetting() +} + type Stub interface { Mock(cond RequestCondition, resp *http.Response, err error) Clear() @@ -31,6 +36,10 @@ type Stub interface { var mockFilter = &MockResponseFilter{} +func InitMockSetting() { + httplib.AddDefaultFilter(mockFilter.FilterChain) +} + func StartMock() Stub { return mockFilter } diff --git a/client/httplib/mock_condition.go b/client/httplib/mock/mock_condition.go similarity index 88% rename from client/httplib/mock_condition.go rename to client/httplib/mock/mock_condition.go index 5e6ff455..639b45a3 100644 --- a/client/httplib/mock_condition.go +++ b/client/httplib/mock/mock_condition.go @@ -12,17 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -package httplib +package mock import ( "context" "encoding/json" "net/textproto" "regexp" + + "github.com/beego/beego/v2/client/httplib" ) type RequestCondition interface { - Match(ctx context.Context, req *BeegoHTTPRequest) bool + Match(ctx context.Context, req *httplib.BeegoHTTPRequest) bool } // reqCondition create condition @@ -54,7 +56,7 @@ func NewSimpleCondition(path string, opts ...simpleConditionOption) *SimpleCondi return sc } -func (sc *SimpleCondition) Match(ctx context.Context, req *BeegoHTTPRequest) bool { +func (sc *SimpleCondition) Match(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { res := true if len(sc.path) > 0 { res = sc.matchPath(ctx, req) @@ -70,12 +72,12 @@ func (sc *SimpleCondition) Match(ctx context.Context, req *BeegoHTTPRequest) boo sc.matchBodyFields(ctx, req) } -func (sc *SimpleCondition) matchPath(ctx context.Context, req *BeegoHTTPRequest) bool { +func (sc *SimpleCondition) matchPath(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { path := req.GetRequest().URL.Path return path == sc.path } -func (sc *SimpleCondition) matchPathReg(ctx context.Context, req *BeegoHTTPRequest) bool { +func (sc *SimpleCondition) matchPathReg(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { path := req.GetRequest().URL.Path if b, err := regexp.Match(sc.pathReg, []byte(path)); err == nil { return b @@ -83,7 +85,7 @@ func (sc *SimpleCondition) matchPathReg(ctx context.Context, req *BeegoHTTPReque return false } -func (sc *SimpleCondition) matchQuery(ctx context.Context, req *BeegoHTTPRequest) bool { +func (sc *SimpleCondition) matchQuery(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { qs := req.GetRequest().URL.Query() for k, v := range sc.query { if uv, ok := qs[k]; !ok || uv[0] != v { @@ -93,7 +95,7 @@ func (sc *SimpleCondition) matchQuery(ctx context.Context, req *BeegoHTTPRequest return true } -func (sc *SimpleCondition) matchHeader(ctx context.Context, req *BeegoHTTPRequest) bool { +func (sc *SimpleCondition) matchHeader(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { headers := req.GetRequest().Header for k, v := range sc.header { if uv, ok := headers[k]; !ok || uv[0] != v { @@ -103,7 +105,7 @@ func (sc *SimpleCondition) matchHeader(ctx context.Context, req *BeegoHTTPReques return true } -func (sc *SimpleCondition) matchBodyFields(ctx context.Context, req *BeegoHTTPRequest) bool { +func (sc *SimpleCondition) matchBodyFields(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { if len(sc.body) == 0 { return true } @@ -135,7 +137,7 @@ func (sc *SimpleCondition) matchBodyFields(ctx context.Context, req *BeegoHTTPRe return true } -func (sc *SimpleCondition) matchMethod(ctx context.Context, req *BeegoHTTPRequest) bool { +func (sc *SimpleCondition) matchMethod(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { if len(sc.method) > 0 { return sc.method == req.GetRequest().Method } diff --git a/client/httplib/mock_condition_test.go b/client/httplib/mock/mock_condition_test.go similarity index 74% rename from client/httplib/mock_condition_test.go rename to client/httplib/mock/mock_condition_test.go index 643dc353..4fc6d377 100644 --- a/client/httplib/mock_condition_test.go +++ b/client/httplib/mock/mock_condition_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package httplib +package mock import ( "context" @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/beego/beego/v2/client/httplib" ) func init() { @@ -28,37 +29,37 @@ func init() { func TestSimpleCondition_MatchPath(t *testing.T) { sc := NewSimpleCondition("/abc/s") - res := sc.Match(context.Background(), Get("http://localhost:8080/abc/s")) + res := sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s")) assert.True(t, res) } func TestSimpleCondition_MatchQuery(t *testing.T) { k, v := "my-key", "my-value" sc := NewSimpleCondition("/abc/s") - res := sc.Match(context.Background(), Get("http://localhost:8080/abc/s?my-key=my-value")) + res := sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-value")) assert.True(t, res) sc = NewSimpleCondition("/abc/s", WithQuery(k, v)) - res = sc.Match(context.Background(), Get("http://localhost:8080/abc/s?my-key=my-value")) + res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-value")) assert.True(t, res) - res = sc.Match(context.Background(), Get("http://localhost:8080/abc/s?my-key=my-valuesss")) + res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-valuesss")) assert.False(t, res) - res = sc.Match(context.Background(), Get("http://localhost:8080/abc/s?my-key-a=my-value")) + res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key-a=my-value")) assert.False(t, res) - res = sc.Match(context.Background(), Get("http://localhost:8080/abc/s?my-key=my-value&abc=hello")) + res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-value&abc=hello")) assert.True(t, res) } func TestSimpleCondition_MatchHeader(t *testing.T) { k, v := "my-header", "my-header-value" sc := NewSimpleCondition("/abc/s") - req := Get("http://localhost:8080/abc/s") + req := httplib.Get("http://localhost:8080/abc/s") assert.True(t, sc.Match(context.Background(), req)) - req = Get("http://localhost:8080/abc/s") + req = httplib.Get("http://localhost:8080/abc/s") req.Header(k, v) assert.True(t, sc.Match(context.Background(), req)) @@ -73,7 +74,7 @@ func TestSimpleCondition_MatchHeader(t *testing.T) { func TestSimpleCondition_MatchBodyField(t *testing.T) { sc := NewSimpleCondition("/abc/s") - req := Post("http://localhost:8080/abc/s") + req := httplib.Post("http://localhost:8080/abc/s") assert.True(t, sc.Match(context.Background(), req)) @@ -102,7 +103,7 @@ func TestSimpleCondition_MatchBodyField(t *testing.T) { func TestSimpleCondition_Match(t *testing.T) { sc := NewSimpleCondition("/abc/s") - req := Post("http://localhost:8080/abc/s") + req := httplib.Post("http://localhost:8080/abc/s") assert.True(t, sc.Match(context.Background(), req)) @@ -115,9 +116,9 @@ func TestSimpleCondition_Match(t *testing.T) { func TestSimpleCondition_MatchPathReg(t *testing.T) { sc := NewSimpleCondition("", WithPathReg(`\/abc\/.*`)) - req := Post("http://localhost:8080/abc/s") + req := httplib.Post("http://localhost:8080/abc/s") assert.True(t, sc.Match(context.Background(), req)) - req = Post("http://localhost:8080/abcd/s") + req = httplib.Post("http://localhost:8080/abcd/s") assert.False(t, sc.Match(context.Background(), req)) } diff --git a/client/httplib/mock_filter.go b/client/httplib/mock/mock_filter.go similarity index 87% rename from client/httplib/mock_filter.go rename to client/httplib/mock/mock_filter.go index 83a7b71b..225d65f3 100644 --- a/client/httplib/mock_filter.go +++ b/client/httplib/mock/mock_filter.go @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package httplib +package mock import ( "context" - "fmt" "net/http" + + "github.com/beego/beego/v2/client/httplib" ) // MockResponse will return mock response if find any suitable mock data @@ -32,13 +33,10 @@ func NewMockResponseFilter() *MockResponseFilter { } } -func (m *MockResponseFilter) FilterChain(next Filter) Filter { - return func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) { - +func (m *MockResponseFilter) FilterChain(next httplib.Filter) httplib.Filter { + return func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) { ms := mockFromCtx(ctx) ms = append(ms, m.ms...) - - fmt.Printf("url: %s, mock: %d \n", req.url, len(ms)) for _, mock := range ms { if mock.cond.Match(ctx, req) { return mock.resp, mock.err diff --git a/client/httplib/mock_filter_test.go b/client/httplib/mock/mock_filter_test.go similarity index 79% rename from client/httplib/mock_filter_test.go rename to client/httplib/mock/mock_filter_test.go index 40a2185e..b27e772e 100644 --- a/client/httplib/mock_filter_test.go +++ b/client/httplib/mock/mock_filter_test.go @@ -12,20 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package httplib +package mock import ( "errors" "testing" "github.com/stretchr/testify/assert" + + "github.com/beego/beego/v2/client/httplib" ) func TestMockResponseFilter_FilterChain(t *testing.T) { - req := Get("http://localhost:8080/abc/s") + req := httplib.Get("http://localhost:8080/abc/s") ft := NewMockResponseFilter() - expectedResp := NewHttpResponseWithJsonBody(`{}`) + expectedResp := httplib.NewHttpResponseWithJsonBody(`{}`) expectedErr := errors.New("expected error") ft.Mock(NewSimpleCondition("/abc/s"), expectedResp, expectedErr) @@ -35,16 +37,16 @@ func TestMockResponseFilter_FilterChain(t *testing.T) { assert.Equal(t, expectedErr, err) assert.Equal(t, expectedResp, resp) - req = Get("http://localhost:8080/abcd/s") + req = httplib.Get("http://localhost:8080/abcd/s") req.AddFilters(ft.FilterChain) resp, err = req.DoRequest() assert.NotEqual(t, expectedErr, err) assert.NotEqual(t, expectedResp, resp) - req = Get("http://localhost:8080/abc/s") + req = httplib.Get("http://localhost:8080/abc/s") req.AddFilters(ft.FilterChain) - expectedResp1 := NewHttpResponseWithJsonBody(map[string]string{}) + expectedResp1 := httplib.NewHttpResponseWithJsonBody(map[string]string{}) expectedErr1 := errors.New("expected error") ft.Mock(NewSimpleCondition("/abc/abs/bbc"), expectedResp1, expectedErr1) @@ -52,7 +54,7 @@ func TestMockResponseFilter_FilterChain(t *testing.T) { assert.Equal(t, expectedErr, err) assert.Equal(t, expectedResp, resp) - req = Get("http://localhost:8080/abc/abs/bbc") + req = httplib.Get("http://localhost:8080/abc/abs/bbc") req.AddFilters(ft.FilterChain) ft.Mock(NewSimpleCondition("/abc/abs/bbc"), expectedResp1, expectedErr1) resp, err = req.DoRequest() diff --git a/client/httplib/mock_test.go b/client/httplib/mock/mock_test.go similarity index 75% rename from client/httplib/mock_test.go rename to client/httplib/mock/mock_test.go index 1d913b29..e73e8a6a 100644 --- a/client/httplib/mock_test.go +++ b/client/httplib/mock/mock_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package httplib +package mock import ( "context" @@ -21,16 +21,18 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/beego/beego/v2/client/httplib" ) func TestStartMock(t *testing.T) { - defaultSetting.FilterChains = []FilterChain{mockFilter.FilterChain} + // httplib.defaultSetting.FilterChains = []httplib.FilterChain{mockFilter.FilterChain} stub := StartMock() // defer stub.Clear() - expectedResp := NewHttpResponseWithJsonBody([]byte(`{}`)) + expectedResp := httplib.NewHttpResponseWithJsonBody([]byte(`{}`)) expectedErr := errors.New("expected err") stub.Mock(NewSimpleCondition("/abc"), expectedResp, expectedErr) @@ -45,14 +47,14 @@ func TestStartMock(t *testing.T) { // TestStartMock_Isolation Test StartMock that // mock only work for this request func TestStartMock_Isolation(t *testing.T) { - defaultSetting.FilterChains = []FilterChain{mockFilter.FilterChain} + // httplib.defaultSetting.FilterChains = []httplib.FilterChain{mockFilter.FilterChain} // setup global stub stub := StartMock() - globalMockResp := NewHttpResponseWithJsonBody([]byte(`{}`)) + globalMockResp := httplib.NewHttpResponseWithJsonBody([]byte(`{}`)) globalMockErr := errors.New("expected err") stub.Mock(NewSimpleCondition("/abc"), globalMockResp, globalMockErr) - expectedResp := NewHttpResponseWithJsonBody(struct { + expectedResp := httplib.NewHttpResponseWithJsonBody(struct { A string `json:"a"` }{ A: "aaa", @@ -67,9 +69,9 @@ func TestStartMock_Isolation(t *testing.T) { } func OriginnalCodeUsingHttplibPassCtx(ctx context.Context) (*http.Response, error) { - return Get("http://localhost:7777/abc").DoRequestWithCtx(ctx) + return httplib.Get("http://localhost:7777/abc").DoRequestWithCtx(ctx) } func OriginalCodeUsingHttplib() (*http.Response, error){ - return Get("http://localhost:7777/abc").DoRequest() + return httplib.Get("http://localhost:7777/abc").DoRequest() } diff --git a/client/httplib/setting.go b/client/httplib/setting.go new file mode 100644 index 00000000..c8d049e0 --- /dev/null +++ b/client/httplib/setting.go @@ -0,0 +1,81 @@ +// Copyright 2020 beego +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httplib + +import ( + "crypto/tls" + "net/http" + "net/http/cookiejar" + "net/url" + "sync" + "time" +) + +// BeegoHTTPSettings is the http.Client setting +type BeegoHTTPSettings struct { + ShowDebug bool + UserAgent string + ConnectTimeout time.Duration + ReadWriteTimeout time.Duration + TLSClientConfig *tls.Config + Proxy func(*http.Request) (*url.URL, error) + Transport http.RoundTripper + CheckRedirect func(req *http.Request, via []*http.Request) error + EnableCookie bool + Gzip bool + DumpBody bool + Retries int // if set to -1 means will retry forever + RetryDelay time.Duration + FilterChains []FilterChain +} + +// createDefaultCookie creates a global cookiejar to store cookies. +func createDefaultCookie() { + settingMutex.Lock() + defer settingMutex.Unlock() + defaultCookieJar, _ = cookiejar.New(nil) +} + +// SetDefaultSetting overwrites default settings +// Keep in mind that when you invoke the SetDefaultSetting +// some methods invoked before SetDefaultSetting +func SetDefaultSetting(setting BeegoHTTPSettings) { + settingMutex.Lock() + defer settingMutex.Unlock() + defaultSetting = setting +} + +var defaultSetting = BeegoHTTPSettings{ + UserAgent: "beegoServer", + ConnectTimeout: 60 * time.Second, + ReadWriteTimeout: 60 * time.Second, + Gzip: true, + DumpBody: true, + FilterChains: make([]FilterChain, 0, 4), +} + +var defaultCookieJar http.CookieJar +var settingMutex sync.Mutex + +// AddDefaultFilter add a new filter into defaultSetting +// Be careful about using this method if you invoke SetDefaultSetting somewhere +func AddDefaultFilter(fc FilterChain) { + settingMutex.Lock() + defer settingMutex.Unlock() + if defaultSetting.FilterChains == nil { + defaultSetting.FilterChains = make([]FilterChain, 0, 4) + } + defaultSetting.FilterChains = append(defaultSetting.FilterChains, fc) +} \ No newline at end of file diff --git a/core/logs/formatter.go b/core/logs/formatter.go index 67500b2b..80b30fa0 100644 --- a/core/logs/formatter.go +++ b/core/logs/formatter.go @@ -69,8 +69,8 @@ func (p *PatternLogFormatter) ToString(lm *LogMsg) string { 'm': lm.Msg, 'n': strconv.Itoa(lm.LineNumber), 'l': strconv.Itoa(lm.Level), - 't': levelPrefix[lm.Level-1], - 'T': levelNames[lm.Level-1], + 't': levelPrefix[lm.Level], + 'T': levelNames[lm.Level], 'F': lm.FilePath, } _, m['f'] = path.Split(lm.FilePath) diff --git a/core/logs/formatter_test.go b/core/logs/formatter_test.go index a97765ac..a1853d72 100644 --- a/core/logs/formatter_test.go +++ b/core/logs/formatter_test.go @@ -88,7 +88,7 @@ func TestPatternLogFormatter(t *testing.T) { } got := tes.ToString(lm) want := lm.FilePath + ":" + strconv.Itoa(lm.LineNumber) + "|" + - when.Format(tes.WhenFormat) + levelPrefix[lm.Level-1] + ">> " + lm.Msg + when.Format(tes.WhenFormat) + levelPrefix[lm.Level] + ">> " + lm.Msg if got != want { t.Errorf("want %s, got %s", want, got) } diff --git a/server/web/admin.go b/server/web/admin.go index 89c9ddb9..d640c1be 100644 --- a/server/web/admin.go +++ b/server/web/admin.go @@ -112,13 +112,13 @@ func registerAdmin() error { HttpServer: NewHttpServerWithCfg(BConfig), } // keep in mind that all data should be html escaped to avoid XSS attack - beeAdminApp.Router("/", c, "get:AdminIndex") - beeAdminApp.Router("/qps", c, "get:QpsIndex") - beeAdminApp.Router("/prof", c, "get:ProfIndex") - beeAdminApp.Router("/healthcheck", c, "get:Healthcheck") - beeAdminApp.Router("/task", c, "get:TaskStatus") - beeAdminApp.Router("/listconf", c, "get:ListConf") - beeAdminApp.Router("/metrics", c, "get:PrometheusMetrics") + beeAdminApp.Router("/", c, SetRouterMethods(c, "get:AdminIndex")) + beeAdminApp.Router("/qps", c, SetRouterMethods(c, "get:QpsIndex")) + beeAdminApp.Router("/prof", c, SetRouterMethods(c, "get:ProfIndex")) + beeAdminApp.Router("/healthcheck", c, SetRouterMethods(c, "get:Healthcheck")) + beeAdminApp.Router("/task", c, SetRouterMethods(c, "get:TaskStatus")) + beeAdminApp.Router("/listconf", c, SetRouterMethods(c, "get:ListConf")) + beeAdminApp.Router("/metrics", c, SetRouterMethods(c, "get:PrometheusMetrics")) go beeAdminApp.Run() } diff --git a/server/web/flash_test.go b/server/web/flash_test.go index 2deef54e..c1ca9554 100644 --- a/server/web/flash_test.go +++ b/server/web/flash_test.go @@ -40,7 +40,7 @@ func TestFlashHeader(t *testing.T) { // setup the handler handler := NewControllerRegister() - handler.Add("/", &TestFlashController{}, "get:TestWriteFlash") + handler.Add("/", &TestFlashController{}, SetRouterMethods(&TestFlashController{}, "get:TestWriteFlash")) handler.ServeHTTP(w, r) // get the Set-Cookie value diff --git a/server/web/namespace.go b/server/web/namespace.go index 4e0c3b85..3598a222 100644 --- a/server/web/namespace.go +++ b/server/web/namespace.go @@ -99,7 +99,7 @@ func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace { // Router same as beego.Rourer // refer: https://godoc.org/github.com/beego/beego/v2#Router func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { - n.handlers.Add(rootpath, c, mappingMethods...) + n.handlers.Add(rootpath, c, SetRouterMethods(c, mappingMethods...)) return n } diff --git a/server/web/router.go b/server/web/router.go index fd94a769..ba85ad6e 100644 --- a/server/web/router.go +++ b/server/web/router.go @@ -118,12 +118,27 @@ type ControllerInfo struct { routerType int initialize func() ControllerInterface methodParams []*param.MethodParam + sessionOn bool } +type ControllerOptions func(*ControllerInfo) + func (c *ControllerInfo) GetPattern() string { return c.pattern } +func SetRouterMethods(ctrlInterface ControllerInterface, mappingMethod ...string) ControllerOptions { + return func(c *ControllerInfo) { + c.methods = parseMappingMethods(ctrlInterface, mappingMethod) + } +} + +func SetRouterSessionOn(sessionOn bool) ControllerOptions { + return func(c *ControllerInfo) { + c.sessionOn = sessionOn + } +} + // ControllerRegister containers registered router rules, controller handlers and filters. type ControllerRegister struct { routers map[string]*Tree @@ -171,40 +186,67 @@ func NewControllerRegisterWithCfg(cfg *Config) *ControllerRegister { // Add("/api/delete",&RestController{},"delete:DeleteFood") // Add("/api",&RestController{},"get,post:ApiFunc" // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") -func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { - p.addWithMethodParams(pattern, c, nil, mappingMethods...) +func (p *ControllerRegister) Add(pattern string, c ControllerInterface, opts ...ControllerOptions) { + p.addWithMethodParams(pattern, c, nil, opts...) } -func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) { +func parseMappingMethods(c ControllerInterface, mappingMethods []string) map[string]string { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() methods := make(map[string]string) - if len(mappingMethods) > 0 { - semi := strings.Split(mappingMethods[0], ";") - for _, v := range semi { - colon := strings.Split(v, ":") - if len(colon) != 2 { - panic("method mapping format is invalid") + + if len(mappingMethods) == 0 { + return methods + } + + semi := strings.Split(mappingMethods[0], ";") + for _, v := range semi { + colon := strings.Split(v, ":") + if len(colon) != 2 { + panic("method mapping format is invalid") + } + comma := strings.Split(colon[0], ",") + for _, m := range comma { + if m != "*" && !HTTPMETHOD[strings.ToUpper(m)] { + panic(v + " is an invalid method mapping. Method doesn't exist " + m) } - comma := strings.Split(colon[0], ",") - for _, m := range comma { - if m == "*" || HTTPMETHOD[strings.ToUpper(m)] { - if val := reflectVal.MethodByName(colon[1]); val.IsValid() { - methods[strings.ToUpper(m)] = colon[1] - } else { - panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name()) - } - } else { - panic(v + " is an invalid method mapping. Method doesn't exist " + m) - } + if val := reflectVal.MethodByName(colon[1]); val.IsValid() { + methods[strings.ToUpper(m)] = colon[1] + continue } + panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name()) } } + return methods +} + +func (p *ControllerRegister) addRouterForMethod(route *ControllerInfo) { + if len(route.methods) == 0 { + for m := range HTTPMETHOD { + p.addToRouter(m, route.pattern, route) + } + return + } + for k := range route.methods { + if k != "*" { + p.addToRouter(k, route.pattern, route) + continue + } + for m := range HTTPMETHOD { + p.addToRouter(m, route.pattern, route) + } + } +} + +func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, opts ...ControllerOptions) { + reflectVal := reflect.ValueOf(c) + t := reflect.Indirect(reflectVal).Type() + route := &ControllerInfo{} route.pattern = pattern - route.methods = methods route.routerType = routerTypeBeego + route.sessionOn = p.cfg.WebConfig.Session.SessionOn route.controllerType = t route.initialize = func() ControllerInterface { vc := reflect.New(route.controllerType) @@ -229,23 +271,18 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt return execController } - route.methodParams = methodParams - if len(methods) == 0 { - for m := range HTTPMETHOD { - p.addToRouter(m, pattern, route) - } - } else { - for k := range methods { - if k == "*" { - for m := range HTTPMETHOD { - p.addToRouter(m, pattern, route) - } - } else { - p.addToRouter(k, pattern, route) - } - } + for i := range opts { + opts[i](route) } + + globalSessionOn := p.cfg.WebConfig.Session.SessionOn + if !globalSessionOn && route.sessionOn { + logs.Warn("global sessionOn is false, sessionOn of router [%s] can't be set to true", route.pattern) + route.sessionOn = globalSessionOn + } + + p.addRouterForMethod(route) } func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) { @@ -273,7 +310,8 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { for _, f := range a.Filters { p.InsertFilter(f.Pattern, f.Pos, f.Filter, WithReturnOnOutput(f.ReturnOnOutput), WithResetParams(f.ResetParams)) } - p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) + + p.addWithMethodParams(a.Router, c, a.MethodParams, SetRouterMethods(c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)) } } } @@ -379,6 +417,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeRESTFul + route.sessionOn = p.cfg.WebConfig.Session.SessionOn route.runFunction = f methods := make(map[string]string) if method == "*" { @@ -399,6 +438,7 @@ func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ... route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeHandler + route.sessionOn = p.cfg.WebConfig.Session.SessionOn route.handler = h if len(options) > 0 { if _, ok := options[0].(bool); ok { @@ -433,6 +473,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) if !utils.InSlice(rt.Method(i).Name, exceptMethod) { route := &ControllerInfo{} route.routerType = routerTypeBeego + route.sessionOn = p.cfg.WebConfig.Session.SessionOn route.methods = map[string]string{"*": rt.Method(i).Name} route.controllerType = ct pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name), "*") @@ -542,7 +583,7 @@ func (p *ControllerRegister) getURL(t *Tree, url, controllerName, methodName str for _, l := range t.leaves { if c, ok := l.runObject.(*ControllerInfo); ok { if c.routerType == routerTypeBeego && - strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllerName) { + strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), `/`+controllerName) { find := false if HTTPMETHOD[strings.ToUpper(methodName)] { if len(c.methods) == 0 { @@ -664,12 +705,15 @@ func (p *ControllerRegister) serveHttp(ctx *beecontext.Context) { r := ctx.Request rw := ctx.ResponseWriter.ResponseWriter var ( - runRouter reflect.Type - findRouter bool - runMethod string - methodParams []*param.MethodParam - routerInfo *ControllerInfo - isRunnable bool + runRouter reflect.Type + findRouter bool + runMethod string + methodParams []*param.MethodParam + routerInfo *ControllerInfo + isRunnable bool + currentSessionOn bool + originRouterInfo *ControllerInfo + originFindRouter bool ) if p.cfg.RecoverFunc != nil { @@ -735,7 +779,12 @@ func (p *ControllerRegister) serveHttp(ctx *beecontext.Context) { } // session init - if p.cfg.WebConfig.Session.SessionOn { + currentSessionOn = p.cfg.WebConfig.Session.SessionOn + originRouterInfo, originFindRouter = p.FindRouter(ctx) + if originFindRouter { + currentSessionOn = originRouterInfo.sessionOn + } + if currentSessionOn { ctx.Input.CruSession, err = GlobalSessions.SessionStart(rw, r) if err != nil { logs.Error(err) diff --git a/server/web/router_test.go b/server/web/router_test.go index 87997322..bd3953ba 100644 --- a/server/web/router_test.go +++ b/server/web/router_test.go @@ -26,6 +26,14 @@ import ( "github.com/beego/beego/v2/server/web/context" ) +type PrefixTestController struct { + Controller +} + +func (ptc *PrefixTestController) PrefixList() { + ptc.Ctx.Output.Body([]byte("i am list in prefix test")) +} + type TestController struct { Controller } @@ -87,10 +95,24 @@ func (jc *JSONController) Get() { jc.Ctx.Output.Body([]byte("ok")) } +func TestPrefixUrlFor(t *testing.T){ + handler := NewControllerRegister() + handler.Add("/my/prefix/list", &PrefixTestController{}, "get:PrefixList") + + if a := handler.URLFor(`PrefixTestController.PrefixList`); a != `/my/prefix/list` { + logs.Info(a) + t.Errorf("PrefixTestController.PrefixList must equal to /my/prefix/list") + } + if a := handler.URLFor(`TestController.PrefixList`); a != `` { + logs.Info(a) + t.Errorf("TestController.PrefixList must equal to empty string") + } +} + func TestUrlFor(t *testing.T) { handler := NewControllerRegister() - handler.Add("/api/list", &TestController{}, "*:List") - handler.Add("/person/:last/:first", &TestController{}, "*:Param") + handler.Add("/api/list", &TestController{}, SetRouterMethods(&TestController{}, "*:List")) + handler.Add("/person/:last/:first", &TestController{}, SetRouterMethods(&TestController{}, "*:Param")) if a := handler.URLFor("TestController.List"); a != "/api/list" { logs.Info(a) t.Errorf("TestController.List must equal to /api/list") @@ -113,9 +135,9 @@ func TestUrlFor3(t *testing.T) { func TestUrlFor2(t *testing.T) { handler := NewControllerRegister() - handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, "*:List") - handler.Add("/v1/:username/edit", &TestController{}, "get:GetURL") - handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param") + handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, SetRouterMethods(&TestController{}, "*:List")) + handler.Add("/v1/:username/edit", &TestController{}, SetRouterMethods(&TestController{}, "get:GetURL")) + handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, SetRouterMethods(&TestController{}, "*:Param")) handler.Add("/:year:int/:month:int/:title/:entid", &TestController{}) if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" { logs.Info(handler.URLFor("TestController.GetURL")) @@ -145,7 +167,7 @@ func TestUserFunc(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/api/list", &TestController{}, "*:List") + handler.Add("/api/list", &TestController{}, SetRouterMethods(&TestController{}, "*:List")) handler.ServeHTTP(w, r) if w.Body.String() != "i am list" { t.Errorf("user define func can't run") @@ -235,7 +257,7 @@ func TestRouteOk(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/person/:last/:first", &TestController{}, "get:GetParams") + handler.Add("/person/:last/:first", &TestController{}, SetRouterMethods(&TestController{}, "get:GetParams")) handler.ServeHTTP(w, r) body := w.Body.String() if body != "anderson+thomas+kungfu" { @@ -249,7 +271,7 @@ func TestManyRoute(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, "get:GetManyRouter") + handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, SetRouterMethods(&TestController{}, "get:GetManyRouter")) handler.ServeHTTP(w, r) body := w.Body.String() @@ -266,7 +288,7 @@ func TestEmptyResponse(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/beego-empty.html", &TestController{}, "get:GetEmptyBody") + handler.Add("/beego-empty.html", &TestController{}, SetRouterMethods(&TestController{}, "get:GetEmptyBody")) handler.ServeHTTP(w, r) if body := w.Body.String(); body != "" { @@ -750,3 +772,59 @@ func TestRouterEntityTooLargeCopyBody(t *testing.T) { t.Errorf("TestRouterRequestEntityTooLarge can't run") } } + +func TestRouterSessionSet(t *testing.T) { + oldGlobalSessionOn := BConfig.WebConfig.Session.SessionOn + defer func() { + BConfig.WebConfig.Session.SessionOn = oldGlobalSessionOn + }() + + // global sessionOn = false, router sessionOn = false + r, _ := http.NewRequest("GET", "/user", nil) + w := httptest.NewRecorder() + handler := NewControllerRegister() + handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), + SetRouterSessionOn(false)) + handler.ServeHTTP(w, r) + if w.Header().Get("Set-Cookie") != "" { + t.Errorf("TestRotuerSessionSet failed") + } + + // global sessionOn = false, router sessionOn = true + r, _ = http.NewRequest("GET", "/user", nil) + w = httptest.NewRecorder() + handler = NewControllerRegister() + handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), + SetRouterSessionOn(true)) + handler.ServeHTTP(w, r) + if w.Header().Get("Set-Cookie") != "" { + t.Errorf("TestRotuerSessionSet failed") + } + + BConfig.WebConfig.Session.SessionOn = true + if err := registerSession(); err != nil { + t.Errorf("register session failed, error: %s", err.Error()) + } + // global sessionOn = true, router sessionOn = false + r, _ = http.NewRequest("GET", "/user", nil) + w = httptest.NewRecorder() + handler = NewControllerRegister() + handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), + SetRouterSessionOn(false)) + handler.ServeHTTP(w, r) + if w.Header().Get("Set-Cookie") != "" { + t.Errorf("TestRotuerSessionSet failed") + } + + // global sessionOn = true, router sessionOn = true + r, _ = http.NewRequest("GET", "/user", nil) + w = httptest.NewRecorder() + handler = NewControllerRegister() + handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), + SetRouterSessionOn(true)) + handler.ServeHTTP(w, r) + if w.Header().Get("Set-Cookie") == "" { + t.Errorf("TestRotuerSessionSet failed") + } + +} diff --git a/server/web/server.go b/server/web/server.go index f0a4f4ea..280828ff 100644 --- a/server/web/server.go +++ b/server/web/server.go @@ -266,8 +266,8 @@ func (app *HttpServer) Run(addr string, mws ...MiddleWare) { } // Router see HttpServer.Router -func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *HttpServer { - return BeeApp.Router(rootpath, c, mappingMethods...) +func Router(rootpath string, c ControllerInterface, opts ...ControllerOptions) *HttpServer { + return BeeApp.Router(rootpath, c, opts...) } // Router adds a patterned controller handler to BeeApp. @@ -286,8 +286,8 @@ func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *H // beego.Router("/api/create",&RestController{},"post:CreateFood") // beego.Router("/api/update",&RestController{},"put:UpdateFood") // beego.Router("/api/delete",&RestController{},"delete:DeleteFood") -func (app *HttpServer) Router(rootPath string, c ControllerInterface, mappingMethods ...string) *HttpServer { - app.Handlers.Add(rootPath, c, mappingMethods...) +func (app *HttpServer) Router(rootPath string, c ControllerInterface, opts ...ControllerOptions) *HttpServer { + app.Handlers.Add(rootPath, c, opts...) return app } diff --git a/server/web/tree.go b/server/web/tree.go index dc459c49..5a765fd3 100644 --- a/server/web/tree.go +++ b/server/web/tree.go @@ -210,9 +210,9 @@ func (t *Tree) AddRouter(pattern string, runObject interface{}) { func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) { if len(segments) == 0 { if reg != "" { - t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")}) + t.leaves = append([]*leafInfo{{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")}}, t.leaves...) } else { - t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) + t.leaves = append([]*leafInfo{{runObject: route, wildcards: wildcards}}, t.leaves...) } } else { seg := segments[0] diff --git a/server/web/tree_test.go b/server/web/tree_test.go index 3cb39c60..43511ad8 100644 --- a/server/web/tree_test.go +++ b/server/web/tree_test.go @@ -90,7 +90,17 @@ func init() { routers = append(routers, matchTestInfo("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"})) routers = append(routers, matchTestInfo("/api/projects/:pid/members/?:mid", "/api/projects/1/members", map[string]string{":pid": "1"})) routers = append(routers, matchTestInfo("/api/projects/:pid/members/?:mid", "/api/projects/1/members/2", map[string]string{":pid": "1", ":mid": "2"})) - + routers = append(routers, matchTestInfo("/?:year/?:month/?:day", "/2020/11/10", map[string]string{":year": "2020", ":month": "11", ":day": "10"})) + routers = append(routers, matchTestInfo("/?:year/?:month/?:day", "/2020/11", map[string]string{":year": "2020", ":month": "11"})) + routers = append(routers, matchTestInfo("/?:year", "/2020", map[string]string{":year": "2020"})) + routers = append(routers, matchTestInfo("/?:year([0-9]+)/?:month([0-9]+)/mid/?:day([0-9]+)/?:hour([0-9]+)", "/2020/11/mid/10/24", map[string]string{":year": "2020", ":month": "11", ":day": "10", ":hour": "24"})) + routers = append(routers, matchTestInfo("/?:year/?:month/mid/?:day/?:hour", "/2020/mid/10", map[string]string{":year": "2020", ":day": "10"})) + routers = append(routers, matchTestInfo("/?:year/?:month/mid/?:day/?:hour", "/2020/11/mid", map[string]string{":year": "2020", ":month": "11"})) + routers = append(routers, matchTestInfo("/?:year/?:month/mid/?:day/?:hour", "/mid/10/24", map[string]string{":day": "10", ":hour": "24"})) + routers = append(routers, matchTestInfo("/?:year([0-9]+)/:month([0-9]+)/mid/:day([0-9]+)/?:hour([0-9]+)", "/2020/11/mid/10/24", map[string]string{":year": "2020", ":month": "11", ":day": "10", ":hour": "24"})) + routers = append(routers, matchTestInfo("/?:year/:month/mid/:day/?:hour", "/11/mid/10/24", map[string]string{":month": "11", ":day": "10"})) + routers = append(routers, matchTestInfo("/?:year/:month/mid/:day/?:hour", "/2020/11/mid/10", map[string]string{":year": "2020", ":month": "11", ":day": "10"})) + routers = append(routers, matchTestInfo("/?:year/:month/mid/:day/?:hour", "/11/mid/10", map[string]string{":month": "11", ":day": "10"})) // not match example // https://github.com/beego/beego/v2/issues/3865 diff --git a/server/web/unregroute_test.go b/server/web/unregroute_test.go index c675ae7d..9745dbac 100644 --- a/server/web/unregroute_test.go +++ b/server/web/unregroute_test.go @@ -75,9 +75,9 @@ func TestUnregisterFixedRouteRoot(t *testing.T) { var method = "GET" handler := NewControllerRegister() - handler.Add("/", &TestPreUnregController{}, "get:GetFixedRoot") - handler.Add("/level1", &TestPreUnregController{}, "get:GetFixedLevel1") - handler.Add("/level1/level2", &TestPreUnregController{}, "get:GetFixedLevel2") + handler.Add("/", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) + handler.Add("/level1", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) + handler.Add("/level1/level2", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) // Test original root testHelperFnContentCheck(t, handler, "Test original root", @@ -96,7 +96,7 @@ func TestUnregisterFixedRouteRoot(t *testing.T) { // Replace the root path TestPreUnregController action with the action from // TestPostUnregController - handler.Add("/", &TestPostUnregController{}, "get:GetFixedRoot") + handler.Add("/", &TestPostUnregController{}, SetRouterMethods(&TestPostUnregController{}, "get:GetFixedRoot")) // Test replacement root (expect change) testHelperFnContentCheck(t, handler, "Test replacement root (expect change)", method, "/", contentRootReplacement) @@ -117,9 +117,9 @@ func TestUnregisterFixedRouteLevel1(t *testing.T) { var method = "GET" handler := NewControllerRegister() - handler.Add("/", &TestPreUnregController{}, "get:GetFixedRoot") - handler.Add("/level1", &TestPreUnregController{}, "get:GetFixedLevel1") - handler.Add("/level1/level2", &TestPreUnregController{}, "get:GetFixedLevel2") + handler.Add("/", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) + handler.Add("/level1", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) + handler.Add("/level1/level2", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) // Test original root testHelperFnContentCheck(t, handler, @@ -146,7 +146,7 @@ func TestUnregisterFixedRouteLevel1(t *testing.T) { // Replace the "level1" path TestPreUnregController action with the action from // TestPostUnregController - handler.Add("/level1", &TestPostUnregController{}, "get:GetFixedLevel1") + handler.Add("/level1", &TestPostUnregController{}, SetRouterMethods(&TestPostUnregController{}, "get:GetFixedLevel1")) // Test replacement root (expect no change from the original) testHelperFnContentCheck(t, handler, "Test replacement root (expect no change from the original)", method, "/", contentRootOriginal) @@ -167,9 +167,9 @@ func TestUnregisterFixedRouteLevel2(t *testing.T) { var method = "GET" handler := NewControllerRegister() - handler.Add("/", &TestPreUnregController{}, "get:GetFixedRoot") - handler.Add("/level1", &TestPreUnregController{}, "get:GetFixedLevel1") - handler.Add("/level1/level2", &TestPreUnregController{}, "get:GetFixedLevel2") + handler.Add("/", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) + handler.Add("/level1", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) + handler.Add("/level1/level2", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) // Test original root testHelperFnContentCheck(t, handler, @@ -196,7 +196,7 @@ func TestUnregisterFixedRouteLevel2(t *testing.T) { // Replace the "/level1/level2" path TestPreUnregController action with the action from // TestPostUnregController - handler.Add("/level1/level2", &TestPostUnregController{}, "get:GetFixedLevel2") + handler.Add("/level1/level2", &TestPostUnregController{}, SetRouterMethods(&TestPostUnregController{}, "get:GetFixedLevel2")) // Test replacement root (expect no change from the original) testHelperFnContentCheck(t, handler, "Test replacement root (expect no change from the original)", method, "/", contentRootOriginal)