feat(engine): Added OptionFunc and With (#3572)

* feat: Added `OptionFunc` and `With`

* fix: `With(opts...)` must be after `New`

* feat: improve New with

* fix: test

* optimize code

* optimize nolint

* optimize code

Signed-off-by: Flc゛ <four_leaf_clover@foxmail.com>

---------

Signed-off-by: Flc゛ <four_leaf_clover@foxmail.com>
This commit is contained in:
Flc゛ 2024-03-11 22:35:30 +08:00 committed by GitHub
parent 83fc7673f9
commit ac5e84d93c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 8 deletions

View File

@ -1000,7 +1000,7 @@ func TestContextRenderFile(t *testing.T) {
c.File("./gin.go") c.File("./gin.go")
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "func New() *Engine {") assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16, // Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
// else, Content-Type='text/x-go; charset=utf-8' // else, Content-Type='text/x-go; charset=utf-8'
assert.NotEqual(t, "", w.Header().Get("Content-Type")) assert.NotEqual(t, "", w.Header().Get("Content-Type"))
@ -1014,7 +1014,7 @@ func TestContextRenderFileFromFS(t *testing.T) {
c.FileFromFS("./gin.go", Dir(".", false)) c.FileFromFS("./gin.go", Dir(".", false))
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), "func New() *Engine {") assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
// Content-Type='text/plain; charset=utf-8' when go version <= 1.16, // Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
// else, Content-Type='text/x-go; charset=utf-8' // else, Content-Type='text/x-go; charset=utf-8'
assert.NotEqual(t, "", w.Header().Get("Content-Type")) assert.NotEqual(t, "", w.Header().Get("Content-Type"))
@ -1030,7 +1030,7 @@ func TestContextRenderAttachment(t *testing.T) {
c.FileAttachment("./gin.go", newFilename) c.FileAttachment("./gin.go", newFilename)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), "func New() *Engine {") assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition")) assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
} }
@ -1057,7 +1057,7 @@ func TestContextRenderUTF8Attachment(t *testing.T) {
c.FileAttachment("./gin.go", newFilename) c.FileAttachment("./gin.go", newFilename)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), "func New() *Engine {") assert.Contains(t, w.Body.String(), "func New(opts ...OptionFunc) *Engine {")
assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition")) assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition"))
} }

20
gin.go
View File

@ -47,6 +47,9 @@ var regRemoveRepeatedChar = regexp.MustCompile("/{2,}")
// HandlerFunc defines the handler used by gin middleware as return value. // HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context) type HandlerFunc func(*Context)
// OptionFunc defines the function to change the default configuration
type OptionFunc func(*Engine)
// HandlersChain defines a HandlerFunc slice. // HandlersChain defines a HandlerFunc slice.
type HandlersChain []HandlerFunc type HandlersChain []HandlerFunc
@ -182,7 +185,7 @@ var _ IRouter = (*Engine)(nil)
// - ForwardedByClientIP: true // - ForwardedByClientIP: true
// - UseRawPath: false // - UseRawPath: false
// - UnescapePathValues: true // - UnescapePathValues: true
func New() *Engine { func New(opts ...OptionFunc) *Engine {
debugPrintWARNINGNew() debugPrintWARNINGNew()
engine := &Engine{ engine := &Engine{
RouterGroup: RouterGroup{ RouterGroup: RouterGroup{
@ -211,15 +214,15 @@ func New() *Engine {
engine.pool.New = func() any { engine.pool.New = func() any {
return engine.allocateContext(engine.maxParams) return engine.allocateContext(engine.maxParams)
} }
return engine return engine.With(opts...)
} }
// Default returns an Engine instance with the Logger and Recovery middleware already attached. // Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine { func Default(opts ...OptionFunc) *Engine {
debugPrintWARNINGDefault() debugPrintWARNINGDefault()
engine := New() engine := New()
engine.Use(Logger(), Recovery()) engine.Use(Logger(), Recovery())
return engine return engine.With(opts...)
} }
func (engine *Engine) Handler() http.Handler { func (engine *Engine) Handler() http.Handler {
@ -313,6 +316,15 @@ func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
return engine return engine
} }
// With returns a new Engine instance with the provided options.
func (engine *Engine) With(opts ...OptionFunc) *Engine {
for _, opt := range opts {
opt(engine)
}
return engine
}
func (engine *Engine) rebuild404Handlers() { func (engine *Engine) rebuild404Handlers() {
engine.allNoRoute = engine.combineHandlers(engine.noRoute) engine.allNoRoute = engine.combineHandlers(engine.noRoute)
} }

View File

@ -696,3 +696,37 @@ func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo)
func handlerTest1(c *Context) {} func handlerTest1(c *Context) {}
func handlerTest2(c *Context) {} func handlerTest2(c *Context) {}
func TestNewOptionFunc(t *testing.T) {
var fc = func(e *Engine) {
e.GET("/test1", handlerTest1)
e.GET("/test2", handlerTest2)
e.Use(func(c *Context) {
c.Next()
})
}
r := New(fc)
routes := r.Routes()
assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"})
assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"})
}
func TestWithOptionFunc(t *testing.T) {
r := New()
r.With(func(e *Engine) {
e.GET("/test1", handlerTest1)
e.GET("/test2", handlerTest2)
e.Use(func(c *Context) {
c.Next()
})
})
routes := r.Routes()
assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"})
assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"})
}