New mvc.IgnoreEmbedded option to solve #2103

This commit is contained in:
Gerasimos (Makis) Maropoulos 2023-03-19 16:31:14 +02:00
parent 0954986a66
commit 34387a4a5c
No known key found for this signature in database
GPG Key ID: B9839E9CD30B7B6B
6 changed files with 135 additions and 3 deletions

View File

@ -23,6 +23,36 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
Change applies to `master` branch. 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). - 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. - Remove [ace](https://github.com/eknkc/amber) template parser support, as it was discontinued by its author more than five years ago.

View File

@ -179,7 +179,7 @@ http://localhost:8080/books
</details> </details>
[![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)** ,让您可以轻松地使用此框架。 Iris 有完整且详尽的 **[使用文档](https://www.iris-go.com/#ebookDonateForm)** ,让您可以轻松地使用此框架。

View File

@ -29,7 +29,7 @@ func configure(m *mvc.Application) {
m.Router.Use(cacheHandler) m.Router.Use(cacheHandler)
m.Handle(&exampleController{ m.Handle(&exampleController{
timeFormat: "Mon, Jan 02 2006 15:04:05", timeFormat: "Mon, Jan 02 2006 15:04:05",
}) } /* ,mvc.IgnoreEmbedded --- Can be used to ignore any embedded struct method handlers */)
} }
type exampleController struct { type exampleController struct {

View File

@ -60,6 +60,15 @@ var (
_ AfterActivation = (*ControllerActivator)(nil) _ 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. // ControllerActivator returns a new controller type info description.
// Its functionality can be overridden by the end-dev. // Its functionality can be overridden by the end-dev.
type ControllerActivator struct { 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 // End-devs can change some properties of the *Route on the `BeforeActivator` by using the
// `GetRoute/GetRoutes(functionName)`. // `GetRoute/GetRoutes(functionName)`.
routes map[string][]*router.Route routes map[string][]*router.Route
skipMethodNames []string
// BeginHandlers is a slice of middleware for this controller. // BeginHandlers is a slice of middleware for this controller.
// These handlers will be prependend to each one of // These handlers will be prependend to each one of
// the route that this controller will register(Handle/HandleMany/struct methods) // 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) typ := reflect.TypeOf(controller)
c := &ControllerActivator{ c := &ControllerActivator{
// give access to the Router to the end-devs if they need it for some reason, // give access to the Router to the end-devs if they need it for some reason,
// i.e register done handlers. // i.e register done handlers.
@ -132,6 +142,10 @@ func newControllerActivator(app *Application, controller interface{}) *Controlle
routes: whatReservedMethods(typ), routes: whatReservedMethods(typ),
} }
if IgnoreEmbeddedControllers {
c.SkipEmbeddedMethods()
}
return c return c
} }
@ -157,6 +171,43 @@ func whatReservedMethods(typ reflect.Type) map[string][]*router.Route {
return routes 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. // Name returns the full name of the controller, its package name + the type name.
// Can used at both `BeforeActivation` and `AfterActivation`. // Can used at both `BeforeActivation` and `AfterActivation`.
func (c *ControllerActivator) Name() string { func (c *ControllerActivator) Name() string {
@ -168,6 +219,20 @@ func (c *ControllerActivator) RelName() string {
return strings.TrimPrefix(c.fullName, "main.") 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. // Router is the standard Iris router's public API.
// With this you can register middleware, view layouts, subdomains, serve static files // With this you can register middleware, view layouts, subdomains, serve static files
// and even add custom standard iris handlers as normally. // 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 return false
} }

View File

@ -29,6 +29,13 @@ func newMethodLexer(s string) *methodLexer {
return l return l
} }
/*
var allowedCapitalWords = map[string]struct{}{
"ID": {},
"JSON": {},
}
*/
func (l *methodLexer) reset(s string) { func (l *methodLexer) reset(s string) {
l.cur = -1 l.cur = -1
var words []string var words []string
@ -36,14 +43,31 @@ func (l *methodLexer) reset(s string) {
end := len(s) end := len(s)
start := -1 start := -1
// outter:
for i, n := 0, end; i < n; i++ { for i, n := 0, end; i < n; i++ {
c := rune(s[i]) c := rune(s[i])
if unicode.IsUpper(c) { if unicode.IsUpper(c) {
// it doesn't count the last uppercase // it doesn't count the last uppercase
if start != -1 { 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 end = i
words = append(words, s[start:end]) words = append(words, s[start:end])
} }
start = i start = i
continue continue
} }

View File

@ -196,6 +196,13 @@ func (opt OptionFunc) Apply(c *ControllerActivator) {
opt(c) 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. // Handle serves a controller for the current mvc application's Router.
// It accept any custom struct which its functions will be transformed // It accept any custom struct which its functions will be transformed
// to routes. // to routes.