diff --git a/HISTORY.md b/HISTORY.md index 21cfeac95..f8d75312c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -23,6 +23,36 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene Change applies to `master` branch. +- Add `mvc.IgnoreEmbedded` option to handle [#2103](https://github.com/kataras/iris/issues/2103). Example Code: + +```go +func configure(m *mvc.Application) { + m.Router.Use(cacheHandler) + m.Handle(&exampleController{ + timeFormat: "Mon, Jan 02 2006 15:04:05", + }, mvc.IgnoreEmbedded /* BaseController.GetDoSomething will not be parsed at all */) +} + +type BaseController struct { + Ctx iris.Context +} + +func (c *BaseController) GetDoSomething(i interface{}) error { + return nil +} + +type exampleController struct { + BaseController + + timeFormat string +} + +func (c *exampleController) Get() string { + now := time.Now().Format(c.timeFormat) + return "last time executed without cache: " + now +} +``` + - Add `LoadKV` method on `Iris.Application.I18N` instance. It should be used when no locale files are available. It loads locales via pure Go Map (or database decoded values). - Remove [ace](https://github.com/eknkc/amber) template parser support, as it was discontinued by its author more than five years ago. diff --git a/README_ZH_HANS.md b/README_ZH_HANS.md index 6e6374a43..7bb889504 100644 --- a/README_ZH_HANS.md +++ b/README_ZH_HANS.md @@ -179,7 +179,7 @@ http://localhost:8080/books -[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://bit.ly/2YJeSZe) +[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://replit.com/@kataras/Iris-Hello-World-v1220?v=1) Iris 有完整且详尽的 **[使用文档](https://www.iris-go.com/#ebookDonateForm)** ,让您可以轻松地使用此框架。 diff --git a/_examples/mvc/middleware/main.go b/_examples/mvc/middleware/main.go index 6cd4e53c3..230079ab7 100644 --- a/_examples/mvc/middleware/main.go +++ b/_examples/mvc/middleware/main.go @@ -29,7 +29,7 @@ func configure(m *mvc.Application) { m.Router.Use(cacheHandler) m.Handle(&exampleController{ timeFormat: "Mon, Jan 02 2006 15:04:05", - }) + } /* ,mvc.IgnoreEmbedded --- Can be used to ignore any embedded struct method handlers */) } type exampleController struct { diff --git a/mvc/controller.go b/mvc/controller.go index 30d3d2842..98c9e7696 100644 --- a/mvc/controller.go +++ b/mvc/controller.go @@ -60,6 +60,15 @@ var ( _ AfterActivation = (*ControllerActivator)(nil) ) +// IgnoreEmbeddedControllers is a global variable which indicates whether +// the controller's method parser should skip converting embedded struct's methods to http handlers. +// +// If no global use is necessary, developers can do the same for individual controllers +// through the `IgnoreEmbedded` Controller Option on `mvc.Application.Handle` method. +// +// Defaults to false. +var IgnoreEmbeddedControllers = false + // ControllerActivator returns a new controller type info description. // Its functionality can be overridden by the end-dev. type ControllerActivator struct { @@ -78,6 +87,8 @@ type ControllerActivator struct { // End-devs can change some properties of the *Route on the `BeforeActivator` by using the // `GetRoute/GetRoutes(functionName)`. routes map[string][]*router.Route + + skipMethodNames []string // BeginHandlers is a slice of middleware for this controller. // These handlers will be prependend to each one of // the route that this controller will register(Handle/HandleMany/struct methods) @@ -114,7 +125,6 @@ func newControllerActivator(app *Application, controller interface{}) *Controlle } typ := reflect.TypeOf(controller) - c := &ControllerActivator{ // give access to the Router to the end-devs if they need it for some reason, // i.e register done handlers. @@ -132,6 +142,10 @@ func newControllerActivator(app *Application, controller interface{}) *Controlle routes: whatReservedMethods(typ), } + if IgnoreEmbeddedControllers { + c.SkipEmbeddedMethods() + } + return c } @@ -157,6 +171,43 @@ func whatReservedMethods(typ reflect.Type) map[string][]*router.Route { return routes } +func whatEmbeddedMethods(typ reflect.Type) []string { + var embeddedMethodsToIgnore []string + controllerType := typ + if controllerType.Kind() == reflect.Ptr { + controllerType = controllerType.Elem() + } + + for i := 0; i < controllerType.NumField(); i++ { + structField := controllerType.Field(i) + structType := structField.Type + + if !structField.Anonymous { + continue + } + + // var structValuePtr reflect.Value + + if structType.Kind() == reflect.Ptr { + // keep both ptr and value instances of the struct so we can ignore all of its methods. + structType = structType.Elem() + // structValuePtr = reflect.ValueOf(reflect.ValueOf(controller).Field(i)) + } + + if structType.Kind() != reflect.Struct { + continue + } + + newEmbeddedStructType := reflect.New(structField.Type).Type() + // let's take its methods and add to methods to ignore from the parent, the controller itself. + for j := 0; j < newEmbeddedStructType.NumMethod(); j++ { + embeddedMethodName := newEmbeddedStructType.Method(j).Name + embeddedMethodsToIgnore = append(embeddedMethodsToIgnore, embeddedMethodName) + } + } + return embeddedMethodsToIgnore +} + // Name returns the full name of the controller, its package name + the type name. // Can used at both `BeforeActivation` and `AfterActivation`. func (c *ControllerActivator) Name() string { @@ -168,6 +219,20 @@ func (c *ControllerActivator) RelName() string { return strings.TrimPrefix(c.fullName, "main.") } +// SkipMethods can be used to individually skip one or more controller's method handlers. +func (c *ControllerActivator) SkipMethods(methodNames ...string) { + c.skipMethodNames = append(c.skipMethodNames, methodNames...) +} + +// SkipEmbeddedMethods should be ran before controller parsing. +// It skips all embedded struct's methods conversation to http handlers. +// +// See https://github.com/kataras/iris/issues/2103 for more. +func (c *ControllerActivator) SkipEmbeddedMethods() { + methodsToIgnore := whatEmbeddedMethods(c.Type) + c.SkipMethods(methodsToIgnore...) +} + // Router is the standard Iris router's public API. // With this you can register middleware, view layouts, subdomains, serve static files // and even add custom standard iris handlers as normally. @@ -259,6 +324,12 @@ func (c *ControllerActivator) isReservedMethod(name string) bool { } } + for _, methodName := range c.skipMethodNames { + if methodName == name { + return true + } + } + return false } diff --git a/mvc/controller_method_parser.go b/mvc/controller_method_parser.go index 24e927265..dd8df5a73 100644 --- a/mvc/controller_method_parser.go +++ b/mvc/controller_method_parser.go @@ -29,6 +29,13 @@ func newMethodLexer(s string) *methodLexer { return l } +/* +var allowedCapitalWords = map[string]struct{}{ + "ID": {}, + "JSON": {}, +} +*/ + func (l *methodLexer) reset(s string) { l.cur = -1 var words []string @@ -36,14 +43,31 @@ func (l *methodLexer) reset(s string) { end := len(s) start := -1 + // outter: for i, n := 0, end; i < n; i++ { c := rune(s[i]) if unicode.IsUpper(c) { // it doesn't count the last uppercase if start != -1 { + /* + for allowedCapitalWord := range allowedCapitalWords { + capitalWordEnd := i + len(allowedCapitalWord) // takes last char too, e.g. ReadJSON, we need the JSON. + if len(s) >= capitalWordEnd { + word := s[i:capitalWordEnd] + if word == allowedCapitalWord { + words = append(words, word) + i = capitalWordEnd + start = i + continue outter + } + } + } + */ + end = i words = append(words, s[start:end]) } + start = i continue } diff --git a/mvc/mvc.go b/mvc/mvc.go index 9fd1a9810..bd96076d3 100644 --- a/mvc/mvc.go +++ b/mvc/mvc.go @@ -196,6 +196,13 @@ func (opt OptionFunc) Apply(c *ControllerActivator) { opt(c) } +// IgnoreEmbedded is an Option which can be used to ignore all embedded struct's method handlers. +// +// For global affect, set the `IgnoreEmbeddedControllers` package-level variable to true. +var IgnoreEmbedded OptionFunc = func(c *ControllerActivator) { + c.SkipEmbeddedMethods() +} + // Handle serves a controller for the current mvc application's Router. // It accept any custom struct which its functions will be transformed // to routes.