Skip to content

Commit 34387a4

Browse files
committed
New mvc.IgnoreEmbedded option to solve kataras#2103
1 parent 0954986 commit 34387a4

File tree

6 files changed

+135
-3
lines changed

6 files changed

+135
-3
lines changed

HISTORY.md

+30
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,36 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
2323

2424
Change applies to `master` branch.
2525

26+
- Add `mvc.IgnoreEmbedded` option to handle [#2103](https://github.com/kataras/iris/issues/2103). Example Code:
27+
28+
```go
29+
func configure(m *mvc.Application) {
30+
m.Router.Use(cacheHandler)
31+
m.Handle(&exampleController{
32+
timeFormat: "Mon, Jan 02 2006 15:04:05",
33+
}, mvc.IgnoreEmbedded /* BaseController.GetDoSomething will not be parsed at all */)
34+
}
35+
36+
type BaseController struct {
37+
Ctx iris.Context
38+
}
39+
40+
func (c *BaseController) GetDoSomething(i interface{}) error {
41+
return nil
42+
}
43+
44+
type exampleController struct {
45+
BaseController
46+
47+
timeFormat string
48+
}
49+
50+
func (c *exampleController) Get() string {
51+
now := time.Now().Format(c.timeFormat)
52+
return "last time executed without cache: " + now
53+
}
54+
```
55+
2656
- 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).
2757

2858
- Remove [ace](https://github.com/eknkc/amber) template parser support, as it was discontinued by its author more than five years ago.

README_ZH_HANS.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ http://localhost:8080/books
179179

180180
</details>
181181

182-
[![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)
182+
[![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)
183183

184184
Iris 有完整且详尽的 **[使用文档](https://www.iris-go.com/#ebookDonateForm)** ,让您可以轻松地使用此框架。
185185

_examples/mvc/middleware/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func configure(m *mvc.Application) {
2929
m.Router.Use(cacheHandler)
3030
m.Handle(&exampleController{
3131
timeFormat: "Mon, Jan 02 2006 15:04:05",
32-
})
32+
} /* ,mvc.IgnoreEmbedded --- Can be used to ignore any embedded struct method handlers */)
3333
}
3434

3535
type exampleController struct {

mvc/controller.go

+72-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ var (
6060
_ AfterActivation = (*ControllerActivator)(nil)
6161
)
6262

63+
// IgnoreEmbeddedControllers is a global variable which indicates whether
64+
// the controller's method parser should skip converting embedded struct's methods to http handlers.
65+
//
66+
// If no global use is necessary, developers can do the same for individual controllers
67+
// through the `IgnoreEmbedded` Controller Option on `mvc.Application.Handle` method.
68+
//
69+
// Defaults to false.
70+
var IgnoreEmbeddedControllers = false
71+
6372
// ControllerActivator returns a new controller type info description.
6473
// Its functionality can be overridden by the end-dev.
6574
type ControllerActivator struct {
@@ -78,6 +87,8 @@ type ControllerActivator struct {
7887
// End-devs can change some properties of the *Route on the `BeforeActivator` by using the
7988
// `GetRoute/GetRoutes(functionName)`.
8089
routes map[string][]*router.Route
90+
91+
skipMethodNames []string
8192
// BeginHandlers is a slice of middleware for this controller.
8293
// These handlers will be prependend to each one of
8394
// the route that this controller will register(Handle/HandleMany/struct methods)
@@ -114,7 +125,6 @@ func newControllerActivator(app *Application, controller interface{}) *Controlle
114125
}
115126

116127
typ := reflect.TypeOf(controller)
117-
118128
c := &ControllerActivator{
119129
// give access to the Router to the end-devs if they need it for some reason,
120130
// i.e register done handlers.
@@ -132,6 +142,10 @@ func newControllerActivator(app *Application, controller interface{}) *Controlle
132142
routes: whatReservedMethods(typ),
133143
}
134144

145+
if IgnoreEmbeddedControllers {
146+
c.SkipEmbeddedMethods()
147+
}
148+
135149
return c
136150
}
137151

@@ -157,6 +171,43 @@ func whatReservedMethods(typ reflect.Type) map[string][]*router.Route {
157171
return routes
158172
}
159173

174+
func whatEmbeddedMethods(typ reflect.Type) []string {
175+
var embeddedMethodsToIgnore []string
176+
controllerType := typ
177+
if controllerType.Kind() == reflect.Ptr {
178+
controllerType = controllerType.Elem()
179+
}
180+
181+
for i := 0; i < controllerType.NumField(); i++ {
182+
structField := controllerType.Field(i)
183+
structType := structField.Type
184+
185+
if !structField.Anonymous {
186+
continue
187+
}
188+
189+
// var structValuePtr reflect.Value
190+
191+
if structType.Kind() == reflect.Ptr {
192+
// keep both ptr and value instances of the struct so we can ignore all of its methods.
193+
structType = structType.Elem()
194+
// structValuePtr = reflect.ValueOf(reflect.ValueOf(controller).Field(i))
195+
}
196+
197+
if structType.Kind() != reflect.Struct {
198+
continue
199+
}
200+
201+
newEmbeddedStructType := reflect.New(structField.Type).Type()
202+
// let's take its methods and add to methods to ignore from the parent, the controller itself.
203+
for j := 0; j < newEmbeddedStructType.NumMethod(); j++ {
204+
embeddedMethodName := newEmbeddedStructType.Method(j).Name
205+
embeddedMethodsToIgnore = append(embeddedMethodsToIgnore, embeddedMethodName)
206+
}
207+
}
208+
return embeddedMethodsToIgnore
209+
}
210+
160211
// Name returns the full name of the controller, its package name + the type name.
161212
// Can used at both `BeforeActivation` and `AfterActivation`.
162213
func (c *ControllerActivator) Name() string {
@@ -168,6 +219,20 @@ func (c *ControllerActivator) RelName() string {
168219
return strings.TrimPrefix(c.fullName, "main.")
169220
}
170221

222+
// SkipMethods can be used to individually skip one or more controller's method handlers.
223+
func (c *ControllerActivator) SkipMethods(methodNames ...string) {
224+
c.skipMethodNames = append(c.skipMethodNames, methodNames...)
225+
}
226+
227+
// SkipEmbeddedMethods should be ran before controller parsing.
228+
// It skips all embedded struct's methods conversation to http handlers.
229+
//
230+
// See https://github.com/kataras/iris/issues/2103 for more.
231+
func (c *ControllerActivator) SkipEmbeddedMethods() {
232+
methodsToIgnore := whatEmbeddedMethods(c.Type)
233+
c.SkipMethods(methodsToIgnore...)
234+
}
235+
171236
// Router is the standard Iris router's public API.
172237
// With this you can register middleware, view layouts, subdomains, serve static files
173238
// and even add custom standard iris handlers as normally.
@@ -259,6 +324,12 @@ func (c *ControllerActivator) isReservedMethod(name string) bool {
259324
}
260325
}
261326

327+
for _, methodName := range c.skipMethodNames {
328+
if methodName == name {
329+
return true
330+
}
331+
}
332+
262333
return false
263334
}
264335

mvc/controller_method_parser.go

+24
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,45 @@ func newMethodLexer(s string) *methodLexer {
2929
return l
3030
}
3131

32+
/*
33+
var allowedCapitalWords = map[string]struct{}{
34+
"ID": {},
35+
"JSON": {},
36+
}
37+
*/
38+
3239
func (l *methodLexer) reset(s string) {
3340
l.cur = -1
3441
var words []string
3542
if s != "" {
3643
end := len(s)
3744
start := -1
3845

46+
// outter:
3947
for i, n := 0, end; i < n; i++ {
4048
c := rune(s[i])
4149
if unicode.IsUpper(c) {
4250
// it doesn't count the last uppercase
4351
if start != -1 {
52+
/*
53+
for allowedCapitalWord := range allowedCapitalWords {
54+
capitalWordEnd := i + len(allowedCapitalWord) // takes last char too, e.g. ReadJSON, we need the JSON.
55+
if len(s) >= capitalWordEnd {
56+
word := s[i:capitalWordEnd]
57+
if word == allowedCapitalWord {
58+
words = append(words, word)
59+
i = capitalWordEnd
60+
start = i
61+
continue outter
62+
}
63+
}
64+
}
65+
*/
66+
4467
end = i
4568
words = append(words, s[start:end])
4669
}
70+
4771
start = i
4872
continue
4973
}

mvc/mvc.go

+7
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,13 @@ func (opt OptionFunc) Apply(c *ControllerActivator) {
196196
opt(c)
197197
}
198198

199+
// IgnoreEmbedded is an Option which can be used to ignore all embedded struct's method handlers.
200+
//
201+
// For global affect, set the `IgnoreEmbeddedControllers` package-level variable to true.
202+
var IgnoreEmbedded OptionFunc = func(c *ControllerActivator) {
203+
c.SkipEmbeddedMethods()
204+
}
205+
199206
// Handle serves a controller for the current mvc application's Router.
200207
// It accept any custom struct which its functions will be transformed
201208
// to routes.

0 commit comments

Comments
 (0)