diff --git a/HISTORY.md b/HISTORY.md
index eb6addcd2..39b493959 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -21,6 +21,20 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest`.
+# Su, 16 February 2020 | v12.1.8
+
+New Features:
+
+- [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)
+
+Fixes:
+
+- [App can't find embedded pug template files by go-bindata](https://github.com/kataras/iris/issues/1450)
+
+New Examples:
+
+- [_examples/mvc/grpc-compatible](_examples/mvc/grpc-compatible)
+
# Mo, 10 February 2020 | v12.1.7
Implement **new** `SetRegisterRule(iris.RouteOverride, RouteSkip, RouteError)` to resolve: https://github.com/kataras/iris/issues/1448
diff --git a/HISTORY_ES.md b/HISTORY_ES.md
index 9cf22c7ed..ccdaa84a3 100644
--- a/HISTORY_ES.md
+++ b/HISTORY_ES.md
@@ -21,9 +21,9 @@ Los desarrolladores no están obligados a actualizar si realmente no lo necesita
**Cómo actualizar**: Abra su línea de comandos y ejecute este comando: `go get github.com/kataras/iris/v12@latest`.
-# Mo, 10 February 2020 | v12.1.7
+# Su, 16 February 2020 | v12.1.8
-Not translated yet, please navigate to the [english version](HISTORY.md#mo-10-february-2020--v1217) instead.
+Not translated yet, please navigate to the [english version](HISTORY.md#su-16-february-2020--v1218) instead.
# Sábado, 26 de octubre 2019 | v12.0.0
diff --git a/README.md b/README.md
index 108acb225..8e381fb66 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,11 @@
-
+![](https://iris-go.com/images/sponsor.png) Support your favorite web framework through [Github Sponsors Program](https://github.com/sponsors/kataras)!
+
# Iris Web Framework
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)
diff --git a/VERSION b/VERSION
index 9b41a4e0e..b56d45d59 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.1.7:https://github.com/kataras/iris/releases/tag/v12.1.7
\ No newline at end of file
+12.1.8:https://github.com/kataras/iris/releases/tag/v12.1.8
\ No newline at end of file
diff --git a/_examples/docker/go.mod b/_examples/docker/go.mod
index bc58a1195..1568e55a2 100644
--- a/_examples/docker/go.mod
+++ b/_examples/docker/go.mod
@@ -3,6 +3,6 @@ module app
go 1.13
require (
- github.com/kataras/iris/v12 v12.1.7
+ github.com/kataras/iris/v12 v12.1.8
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
)
diff --git a/_examples/mvc/grpc-compatible/main.go b/_examples/mvc/grpc-compatible/main.go
index 8d775b814..93298b035 100644
--- a/_examples/mvc/grpc-compatible/main.go
+++ b/_examples/mvc/grpc-compatible/main.go
@@ -31,11 +31,19 @@ func newApp() *iris.Application {
return ctx.Request().Context()
}).
// Bind loginRequest.
- Register(func(ctx iris.Context) loginRequest {
- var req loginRequest
- ctx.ReadJSON(&req)
- return req
- }).
+ // Register(func(ctx iris.Context) loginRequest {
+ // var req loginRequest
+ // ctx.ReadJSON(&req)
+ // return req
+ // }).
+ // OR
+ // Bind any other structure or pointer to a structure from request's
+ // XML
+ // YAML
+ // Query
+ // Form
+ // JSON (default, if not client's "Content-Type" specified otherwise)
+ Register(mvc.AutoBinding).
Handle(&myController{})
return app
diff --git a/_examples/mvc/grpc-compatible/main_test.go b/_examples/mvc/grpc-compatible/main_test.go
index cfe369820..2211b5b76 100644
--- a/_examples/mvc/grpc-compatible/main_test.go
+++ b/_examples/mvc/grpc-compatible/main_test.go
@@ -6,7 +6,7 @@ import (
"github.com/kataras/iris/v12/httptest"
)
-func TestBindContextContext(t *testing.T) {
+func TestGRPCCompatible(t *testing.T) {
app := newApp()
e := httptest.New(t, app)
diff --git a/_examples/view/template_pug_3/bindata.go b/_examples/view/template_pug_3/bindata.go
index aaac24f6d..fb9d699af 100644
--- a/_examples/view/template_pug_3/bindata.go
+++ b/_examples/view/template_pug_3/bindata.go
@@ -215,9 +215,9 @@ type bintree struct {
}
var _bintree = &bintree{nil, map[string]*bintree{
- "templates": &bintree{nil, map[string]*bintree{
- "index.pug": &bintree{templatesIndexPug, map[string]*bintree{}},
- "layout.pug": &bintree{templatesLayoutPug, map[string]*bintree{}},
+ "templates": {nil, map[string]*bintree{
+ "index.pug": {templatesIndexPug, map[string]*bintree{}},
+ "layout.pug": {templatesLayoutPug, map[string]*bintree{}},
}},
}}
diff --git a/doc.go b/doc.go
index 3e3ce9399..bf97c9379 100644
--- a/doc.go
+++ b/doc.go
@@ -38,7 +38,7 @@ Source code and other details for the project are available at GitHub:
Current Version
-12.1.7
+12.1.8
Installation
diff --git a/hero/di.go b/hero/di.go
deleted file mode 100644
index 35c8158bd..000000000
--- a/hero/di.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package hero
-
-import (
- "reflect"
-
- "github.com/kataras/iris/v12/context"
- "github.com/kataras/iris/v12/hero/di"
-)
-
-func init() {
- di.DefaultHijacker = func(fieldOrFuncInput reflect.Type) (*di.BindObject, bool) {
- if !IsContext(fieldOrFuncInput) {
- return nil, false
- }
- // this is being used on both func injector and struct injector.
- // if the func's input argument or the struct's field is a type of Context
- // then we can do a fast binding using the ctxValue
- // which is used as slice of reflect.Value, because of the final method's `Call`.
- return &di.BindObject{
- Type: contextTyp,
- BindType: di.Dynamic,
- ReturnValue: func(ctx context.Context) reflect.Value {
- return ctx.ReflectValue()[0]
- },
- }, true
- }
-
- di.DefaultTypeChecker = func(fn reflect.Type) bool {
- // valid if that single input arg is a typeof context.Context
- // or first argument is context.Context and second argument is a variadic, which is ignored (i.e new sessions#Start).
- return (fn.NumIn() == 1 || (fn.NumIn() == 2 && fn.IsVariadic())) && IsContext(fn.In(0))
- }
-
- di.DefaultErrorHandler = di.ErrorHandlerFunc(func(ctx context.Context, err error) {
- if err == nil {
- return
- }
-
- ctx.StatusCode(400)
- ctx.WriteString(err.Error())
- ctx.StopExecution()
- })
-}
diff --git a/hero/di/di.go b/hero/di/di.go
index 9f23ca469..925f221bf 100644
--- a/hero/di/di.go
+++ b/hero/di/di.go
@@ -9,12 +9,6 @@ import (
)
type (
- // Hijacker is a type which is used to catch fields or function's input argument
- // to bind a custom object based on their type.
- Hijacker func(reflect.Type) (*BindObject, bool)
- // TypeChecker checks if a specific field's or function input argument's
- // is valid to be binded.
- TypeChecker func(reflect.Type) bool
// ErrorHandler is the optional interface to handle errors per hero func,
// see `mvc/Application#HandleError` for MVC application-level error handler registration too.
//
@@ -34,15 +28,51 @@ func (fn ErrorHandlerFunc) HandleError(ctx context.Context, err error) {
fn(ctx, err)
}
-var (
- // DefaultHijacker is the hijacker used on the package-level Struct & Func functions.
- DefaultHijacker Hijacker
- // DefaultTypeChecker is the typechecker used on the package-level Struct & Func functions.
- DefaultTypeChecker TypeChecker
- // DefaultErrorHandler is the error handler used on the package-level `Func` function
- // to catch any errors from dependencies or handlers.
- DefaultErrorHandler ErrorHandler
-)
+// DefaultErrorHandler is the default error handler will be fired on
+// any error from registering a request-scoped dynamic dependency and on a controller's method failure.
+var DefaultErrorHandler ErrorHandler = ErrorHandlerFunc(func(ctx context.Context, err error) {
+ if err == nil {
+ return
+ }
+
+ ctx.StatusCode(400)
+ ctx.WriteString(err.Error())
+ ctx.StopExecution()
+})
+
+var emptyValue reflect.Value
+
+// DefaultFallbackBinder used to bind any oprhan inputs. Its error is handled by the `ErrorHandler`.
+var DefaultFallbackBinder FallbackBinder = func(ctx context.Context, input OrphanInput) (newValue reflect.Value, err error) {
+ wasPtr := input.Type.Kind() == reflect.Ptr
+
+ newValue = reflect.New(IndirectType(input.Type))
+ ptr := newValue.Interface()
+
+ switch ctx.GetContentTypeRequested() {
+ case context.ContentXMLHeaderValue:
+ err = ctx.ReadXML(ptr)
+ case context.ContentYAMLHeaderValue:
+ err = ctx.ReadYAML(ptr)
+ case context.ContentFormHeaderValue:
+ err = ctx.ReadQuery(ptr)
+ case context.ContentFormMultipartHeaderValue:
+ err = ctx.ReadForm(ptr)
+ default:
+ err = ctx.ReadJSON(ptr)
+ // json
+ }
+
+ // if err != nil {
+ // return emptyValue, err
+ // }
+
+ if !wasPtr {
+ newValue = newValue.Elem()
+ }
+
+ return newValue, err
+}
// Struct is being used to return a new injector based on
// a struct value instance, if it contains fields that the types of those
@@ -55,8 +85,6 @@ func Struct(s interface{}, values ...reflect.Value) *StructInjector {
return MakeStructInjector(
ValueOf(s),
- DefaultHijacker,
- DefaultTypeChecker,
SortByNumMethods,
Values(values).CloneWithFieldsOf(s)...,
)
@@ -74,9 +102,6 @@ func Func(fn interface{}, values ...reflect.Value) *FuncInjector {
return MakeFuncInjector(
ValueOf(fn),
- DefaultHijacker,
- DefaultTypeChecker,
- DefaultErrorHandler,
values...,
)
}
@@ -88,28 +113,34 @@ func Func(fn interface{}, values ...reflect.Value) *FuncInjector {
type D struct {
Values
- hijacker Hijacker
- goodFunc TypeChecker
- errorHandler ErrorHandler
- sorter Sorter
+ fallbackBinder FallbackBinder
+ errorHandler ErrorHandler
+ sorter Sorter
+}
+
+// OrphanInput represents an input without registered dependency.
+// Used to help the framework (or the caller) auto-resolve it by the request.
+type OrphanInput struct {
+ // Index int // function or struct field index.
+ Type reflect.Type
}
+// FallbackBinder represents a handler of oprhan input values, handler's input arguments or controller's fields.
+type FallbackBinder func(ctx context.Context, input OrphanInput) (reflect.Value, error)
+
// New creates and returns a new Dependency Injection container.
// See `Values` field and `Func` and `Struct` methods for more.
func New() *D {
- return &D{}
-}
-
-// Hijack sets a hijacker function, read the `Hijacker` type for more explanation.
-func (d *D) Hijack(fn Hijacker) *D {
- d.hijacker = fn
- return d
+ return &D{
+ errorHandler: DefaultErrorHandler,
+ fallbackBinder: DefaultFallbackBinder,
+ }
}
-// GoodFunc sets a type checker for a valid function that can be binded,
-// read the `TypeChecker` type for more explanation.
-func (d *D) GoodFunc(fn TypeChecker) *D {
- d.goodFunc = fn
+// FallbackBinder adds a binder which will handle any oprhan input values.
+// See `FallbackBinder` type.
+func (d *D) FallbackBinder(fallbackBinder FallbackBinder) *D {
+ d.fallbackBinder = fallbackBinder
return d
}
@@ -130,11 +161,10 @@ func (d *D) Sort(with Sorter) *D {
// parent's (current "D") hijacker, good func type checker, sorter and all dependencies values.
func (d *D) Clone() *D {
return &D{
- Values: d.Values.Clone(),
- hijacker: d.hijacker,
- goodFunc: d.goodFunc,
- errorHandler: d.errorHandler,
- sorter: d.sorter,
+ Values: d.Values.Clone(),
+ fallbackBinder: d.fallbackBinder,
+ errorHandler: d.errorHandler,
+ sorter: d.sorter,
}
}
@@ -144,16 +174,19 @@ func (d *D) Clone() *D {
// with the injector's `Inject` and `InjectElem` methods.
func (d *D) Struct(s interface{}) *StructInjector {
if s == nil {
- return &StructInjector{Has: false}
+ return &StructInjector{}
}
- return MakeStructInjector(
+ injector := MakeStructInjector(
ValueOf(s),
- d.hijacker,
- d.goodFunc,
d.sorter,
d.Values.CloneWithFieldsOf(s)...,
)
+
+ injector.ErrorHandler = d.errorHandler
+ injector.FallbackBinder = d.fallbackBinder
+
+ return injector
}
// Func is being used to return a new injector based on
@@ -163,14 +196,16 @@ func (d *D) Struct(s interface{}) *StructInjector {
// with the injector's `Inject` method.
func (d *D) Func(fn interface{}) *FuncInjector {
if fn == nil {
- return &FuncInjector{Has: false}
+ return &FuncInjector{}
}
- return MakeFuncInjector(
+ injector := MakeFuncInjector(
ValueOf(fn),
- d.hijacker,
- d.goodFunc,
- d.errorHandler,
d.Values...,
- ).ErrorHandler(d.errorHandler)
+ )
+
+ injector.ErrorHandler = d.errorHandler
+ injector.FallbackBinder = d.fallbackBinder
+
+ return injector
}
diff --git a/hero/di/func.go b/hero/di/func.go
index 76c316107..9289f8c9e 100644
--- a/hero/di/func.go
+++ b/hero/di/func.go
@@ -18,10 +18,10 @@ type (
FuncInjector struct {
// the original function, is being used
// only the .Call, which is referring to the same function, always.
- fn reflect.Value
- typ reflect.Type
- goodFunc TypeChecker
- errorHandler ErrorHandler
+ fn reflect.Value
+ typ reflect.Type
+ FallbackBinder FallbackBinder
+ ErrorHandler ErrorHandler
inputs []*targetFuncInput
// Length is the number of the valid, final binded input arguments.
@@ -51,13 +51,13 @@ func (s *FuncInjector) miss(index int, remaining Values) {
// that the caller should use to bind input arguments of the "fn" function.
//
// The hijack and the goodFunc are optional, the "values" is the dependencies collection.
-func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, errorHandler ErrorHandler, values ...reflect.Value) *FuncInjector {
+func MakeFuncInjector(fn reflect.Value, values ...reflect.Value) *FuncInjector {
typ := IndirectType(fn.Type())
s := &FuncInjector{
- fn: fn,
- typ: typ,
- goodFunc: goodFunc,
- errorHandler: errorHandler,
+ fn: fn,
+ typ: typ,
+ FallbackBinder: DefaultFallbackBinder,
+ ErrorHandler: DefaultErrorHandler,
}
if !IsFunc(typ) {
@@ -71,16 +71,12 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, e
for i := 0; i < n; i++ {
inTyp := typ.In(i)
- if hijack != nil {
- b, ok := hijack(inTyp)
-
- if ok && b != nil {
- s.inputs = append(s.inputs, &targetFuncInput{
- InputIndex: i,
- Object: b,
- })
- continue
- }
+ if b, ok := tryBindContext(inTyp); ok {
+ s.inputs = append(s.inputs, &targetFuncInput{
+ InputIndex: i,
+ Object: b,
+ })
+ continue
}
matched := false
@@ -98,6 +94,50 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, e
break
}
+
+ // TODO: (already working on it) clean up or even re-write the whole di, hero and some of the mvc,
+ // this is a dirty but working-solution for #1449.
+ // Limitations:
+ // - last input argument
+ // - not able to customize it other than DefaultFallbackBinder on MVC (on hero it can be customized)
+ // - the "di" package is now depends on context package which is not an import-cycle issue, it's not imported there.
+ if i == n-1 {
+ if v.Type() == autoBindingTyp && s.FallbackBinder != nil {
+
+ canFallback := true
+ if k := inTyp.Kind(); k == reflect.Ptr {
+ if inTyp.Elem().Kind() != reflect.Struct {
+ canFallback = false
+ }
+ } else if k != reflect.Struct {
+ canFallback = false
+ }
+
+ if canFallback {
+ matched = true
+
+ s.inputs = append(s.inputs, &targetFuncInput{
+ InputIndex: i,
+ Object: &BindObject{
+ Type: inTyp,
+ BindType: Dynamic,
+ ReturnValue: func(ctx context.Context) reflect.Value {
+ value, err := s.FallbackBinder(ctx, OrphanInput{Type: inTyp})
+ if err != nil {
+ if s.ErrorHandler != nil {
+ s.ErrorHandler.HandleError(ctx, err)
+ }
+ }
+
+ return value
+ },
+ },
+ })
+
+ break
+ }
+ }
+ }
}
if !matched {
@@ -108,7 +148,6 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, e
// with different set of binding "values".
s.miss(i, values) // send the remaining dependencies values.
}
-
}
return s
@@ -119,6 +158,11 @@ func (s *FuncInjector) refresh() {
s.Has = s.Length > 0
}
+// AutoBindingValue a fake type to expliclty set the return value of hero.AutoBinding.
+type AutoBindingValue struct{}
+
+var autoBindingTyp = reflect.TypeOf(AutoBindingValue{})
+
func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
defer s.refresh()
@@ -129,31 +173,14 @@ func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
inTyp := s.typ.In(inputIndex)
// the binded values to the func's inputs.
- b, err := MakeBindObject(value, s.goodFunc, s.errorHandler)
+ b, err := MakeBindObject(value, s.ErrorHandler)
if err != nil {
return false
}
- // TODO: expose that (need to push a fix for issue #1450 first)
- if b.Type == reflectValueType {
- b.Type = inTyp
- // returnValue := b.ReturnValue
- b.ReturnValue = func(ctx context.Context) reflect.Value {
- newValue := reflect.New(inTyp)
-
- if err := ctx.ReadJSON(newValue.Interface()); err != nil {
- if s.errorHandler != nil {
- s.errorHandler.HandleError(ctx, err)
- }
- }
-
- return newValue.Elem()
- }
- }
-
if b.IsAssignable(inTyp) {
- // fmt.Printf("binded input index: %d for type: %s and value: %v with pointer: %v\n",
- // i, b.Type.String(), inTyp.String(), inTyp.Pointer())
+ // fmt.Printf("binded input index: %d for type: %s and value: %v with dependency: %v\n",
+ // inputIndex, b.Type.String(), inTyp.String(), b)
s.inputs = append(s.inputs, &targetFuncInput{
InputIndex: inputIndex,
Object: &b,
@@ -164,12 +191,6 @@ func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
return false
}
-// ErrorHandler registers an error handler for this FuncInjector.
-func (s *FuncInjector) ErrorHandler(errorHandler ErrorHandler) *FuncInjector {
- s.errorHandler = errorHandler
- return s
-}
-
// Retry used to add missing dependencies, i.e path parameter builtin bindings if not already exists
// in the `hero.Handler`, once, only for that func injector.
func (s *FuncInjector) Retry(retryFn func(inIndex int, inTyp reflect.Type, remainingValues Values) (reflect.Value, bool)) bool {
diff --git a/hero/di/object.go b/hero/di/object.go
index 96ed4dded..9759187db 100644
--- a/hero/di/object.go
+++ b/hero/di/object.go
@@ -45,10 +45,10 @@ type BindObject struct {
// or the input arguments (if "v.elem()" is func)
// are valid to be included as the final object's dependencies, even if the caller added more
// the "di" is smart enough to select what each "v" needs and what not before serve time.
-func MakeBindObject(v reflect.Value, goodFunc TypeChecker, errorHandler ErrorHandler) (b BindObject, err error) {
+func MakeBindObject(v reflect.Value, errorHandler ErrorHandler) (b BindObject, err error) {
if IsFunc(v) {
b.BindType = Dynamic
- b.ReturnValue, b.Type, err = MakeReturnValue(v, goodFunc, errorHandler)
+ b.ReturnValue, b.Type, err = MakeReturnValue(v, errorHandler)
} else {
b.BindType = Static
b.Type = v.Type()
@@ -58,6 +58,23 @@ func MakeBindObject(v reflect.Value, goodFunc TypeChecker, errorHandler ErrorHan
return
}
+func tryBindContext(fieldOrFuncInput reflect.Type) (*BindObject, bool) {
+ if !IsContext(fieldOrFuncInput) {
+ return nil, false
+ }
+ // this is being used on both func injector and struct injector.
+ // if the func's input argument or the struct's field is a type of Context
+ // then we can do a fast binding using the ctxValue
+ // which is used as slice of reflect.Value, because of the final method's `Call`.
+ return &BindObject{
+ Type: contextTyp,
+ BindType: Dynamic,
+ ReturnValue: func(ctx context.Context) reflect.Value {
+ return ctx.ReflectValue()[0]
+ },
+ }, true
+}
+
var errBad = errors.New("bad")
// MakeReturnValue takes any function
@@ -71,7 +88,7 @@ var errBad = errors.New("bad")
//
// The return type of the "fn" should be a value instance, not a pointer.
// The binder function should return just one value.
-func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker, errorHandler ErrorHandler) (func(ctx context.Context) reflect.Value, reflect.Type, error) {
+func MakeReturnValue(fn reflect.Value, errorHandler ErrorHandler) (func(context.Context) reflect.Value, reflect.Type, error) {
typ := IndirectType(fn.Type())
// invalid if not a func.
@@ -86,10 +103,8 @@ func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker, errorHandler ErrorH
return nil, typ, errBad
}
- if goodFunc != nil {
- if !goodFunc(typ) {
- return nil, typ, errBad
- }
+ if !goodFunc(typ) {
+ return nil, typ, errBad
}
firstOutTyp := typ.Out(0)
@@ -114,17 +129,6 @@ func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker, errorHandler ErrorH
return firstZeroOutVal
}
- // if firstOutTyp == reflectValueType {
- // converted := v.Convert(typ.In(0))
- // fmt.Printf("object.go#124: converted: %#+v\n", converted)
- // return converted //reflect.ValueOf(v.Interface())
- // }
-
- // if v.String() == "" {
- // println("di/object.go: " + v.String())
- // // println("di/object.go: because it's interface{} it should be returned as: " + v.Elem().Type().String() + " and its value: " + v.Elem().Interface().(string))
- // return v.Elem()
- // }
return v
}
diff --git a/hero/di/reflect.go b/hero/di/reflect.go
index ccdc54977..2e3bbd5f8 100644
--- a/hero/di/reflect.go
+++ b/hero/di/reflect.go
@@ -2,6 +2,8 @@ package di
import (
"reflect"
+
+ "github.com/kataras/iris/v12/context"
)
// EmptyIn is just an empty slice of reflect.Value.
@@ -131,6 +133,19 @@ func goodVal(v reflect.Value) bool {
return v.IsValid()
}
+var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem()
+
+// IsContext returns true if the "inTyp" is a type of Context.
+func IsContext(inTyp reflect.Type) bool {
+ return inTyp.Implements(contextTyp)
+}
+
+func goodFunc(fn reflect.Type) bool {
+ // valid if that single input arg is a typeof context.Context
+ // or first argument is context.Context and second argument is a variadic, which is ignored (i.e new sessions#Start).
+ return (fn.NumIn() == 1 || (fn.NumIn() == 2 && fn.IsVariadic())) && IsContext(fn.In(0))
+}
+
// IsFunc returns true if the passed type is function.
func IsFunc(kindable interface {
Kind() reflect.Kind
diff --git a/hero/di/struct.go b/hero/di/struct.go
index 8a165db06..ebcf77c3a 100644
--- a/hero/di/struct.go
+++ b/hero/di/struct.go
@@ -61,6 +61,9 @@ type (
Has bool
CanInject bool // if any bindable fields when the state is NOT singleton.
Scope Scope
+
+ FallbackBinder FallbackBinder
+ ErrorHandler ErrorHandler
}
)
@@ -103,11 +106,13 @@ var SortByNumMethods Sorter = func(t1 reflect.Type, t2 reflect.Type) bool {
// of the "v" struct value or pointer.
//
// The hijack and the goodFunc are optional, the "values" is the dependencies collection.
-func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, sorter Sorter, values ...reflect.Value) *StructInjector {
+func MakeStructInjector(v reflect.Value, sorter Sorter, values ...reflect.Value) *StructInjector {
s := &StructInjector{
initRef: v,
initRefAsSlice: []reflect.Value{v},
elemType: IndirectType(v.Type()),
+ FallbackBinder: DefaultFallbackBinder,
+ ErrorHandler: DefaultErrorHandler,
}
// Optionally check and keep good values only here,
@@ -138,15 +143,12 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
for _, f := range fields {
// fmt.Printf("[%d] field type [%s] value name [%s]\n", idx, f.Type.String(), f.Name)
- if hijack != nil {
- if b, ok := hijack(f.Type); ok && b != nil {
- s.fields = append(s.fields, &targetStructField{
- FieldIndex: f.Index,
- Object: b,
- })
-
- continue
- }
+ if b, ok := tryBindContext(f.Type); ok {
+ s.fields = append(s.fields, &targetStructField{
+ FieldIndex: f.Index,
+ Object: b,
+ })
+ continue
}
var possibleValues []*targetStructField
@@ -157,9 +159,10 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
}
// the binded values to the struct's fields.
- b, err := MakeBindObject(val, goodFunc, nil)
+ b, err := MakeBindObject(val, nil)
if err != nil {
- return s // if error stop here.
+ panic(err)
+ // return s // if error stop here.
}
if b.IsAssignable(f.Type) {
diff --git a/hero/handler.go b/hero/handler.go
index cb646350b..363793697 100644
--- a/hero/handler.go
+++ b/hero/handler.go
@@ -11,31 +11,17 @@ import (
"github.com/kataras/golog"
)
-var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem()
+// var genericFuncTyp = reflect.TypeOf(func(context.Context) reflect.Value { return reflect.Value{} })
-// IsContext returns true if the "inTyp" is a type of Context.
-func IsContext(inTyp reflect.Type) bool {
- return inTyp.Implements(contextTyp)
-}
-
-// var genericFuncTyp = reflect.TypeOf(func(context.Context) interface{} { return nil })
-var genericFuncTyp = reflect.TypeOf(func(context.Context) reflect.Value { return reflect.Value{} })
-
-// IsGenericFunc reports whether the "inTyp" is a type of func(Context) interface{}.
-func IsGenericFunc(inTyp reflect.Type) bool {
- return inTyp == genericFuncTyp
-}
+// // IsGenericFunc reports whether the "inTyp" is a type of func(Context) interface{}.
+// func IsGenericFunc(inTyp reflect.Type) bool {
+// return inTyp == genericFuncTyp
+// }
// checks if "handler" is context.Handler: func(context.Context).
func isContextHandler(handler interface{}) (context.Handler, bool) {
- h, is := handler.(context.Handler)
- if !is {
- fh, is := handler.(func(context.Context))
- if is {
- return fh, is
- }
- }
- return h, is
+ h, ok := handler.(context.Handler)
+ return h, ok
}
func validateHandler(handler interface{}) error {
@@ -72,7 +58,7 @@ func makeHandler(handler interface{}, errorHandler di.ErrorHandler, values ...re
}
funcInjector := di.Func(fn, values...)
- funcInjector.ErrorHandler(errorHandler)
+ funcInjector.ErrorHandler = errorHandler
valid := funcInjector.Length == n
diff --git a/hero/handler_test.go b/hero/handler_test.go
index 3ee73a6bb..fbd286dc4 100644
--- a/hero/handler_test.go
+++ b/hero/handler_test.go
@@ -4,7 +4,6 @@ package hero_test
import (
"fmt"
- "reflect"
"testing"
"github.com/kataras/iris/v12"
@@ -128,37 +127,23 @@ func TestBindFunctionAsFunctionInputArgument(t *testing.T) {
Expect().Status(iris.StatusOK).Body().Equal(expectedUsername)
}
-func TestBindReflectValue(t *testing.T) {
- // TODO: THINK of simplify this,
- // as 'hero' and 'mvc' are not depend on the root kataras/iris/v12 package, smart decision back then.
- // e.g.
- // app := iris.New()
- // app.RegisterDependency(...)
- // app.HandleFunc("GET POST", "/", func(input MyInput) MyOutput {})
- // instead of:
- // app := iris.New()
- // h := hero.New()
- // h.Register(...) or hero.Register for shared deps across Iris different applications.
- // handler := h.Handler(func(input MyInput) MyOutput {})
- // app.HandleMany("GET POST", "/", handler)
-
+func TestAutoBinding(t *testing.T) {
h := New()
- h.Register(func(ctx iris.Context) reflect.Value {
- var v interface{}
- err := ctx.ReadJSON(&v)
- if err != nil {
- t.Fatal(err)
- }
- return reflect.ValueOf(v)
- // return reflect.Value{}
+ h.Register(AutoBinding)
+
+ postHandler := h.Handler(func(input *testUserStruct /* ptr */) string {
+ return input.Username
})
- postHandler := h.Handler(func(input testUserStruct) string {
+
+ postHandler2 := h.Handler(func(input testUserStruct) string {
return input.Username
})
app := iris.New()
app.Post("/", postHandler)
+ app.Post("/2", postHandler2)
e := httptest.New(t, app)
e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().Equal("makis")
+ e.POST("/2").WithJSON(iris.Map{"username": "kataras"}).Expect().Status(httptest.StatusOK).Body().Equal("kataras")
}
diff --git a/hero/hero.go b/hero/hero.go
index 0570be28d..cd1117b6e 100644
--- a/hero/hero.go
+++ b/hero/hero.go
@@ -48,6 +48,12 @@ func (h *Hero) Dependencies() *di.Values {
return &h.values
}
+// AutoBinding used to be registered as dependency to try to automatically
+// map and bind the inputs that are not already binded with a dependency.
+//
+// See `DefaultFallbackBinder`.
+var AutoBinding = di.AutoBindingValue{}
+
// Register adds one or more values as dependencies.
// The value can be a single struct value-instance or a function
// which has one input and one output, the input should be
diff --git a/iris.go b/iris.go
index 268d4be6e..1b3bbf015 100644
--- a/iris.go
+++ b/iris.go
@@ -41,7 +41,7 @@ import (
)
// Version is the current version number of the Iris Web Framework.
-const Version = "12.1.7"
+const Version = "12.1.8"
// HTTP status codes as registered with IANA.
// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml.
diff --git a/mvc/controller.go b/mvc/controller.go
index 63f43404c..0137f3140 100644
--- a/mvc/controller.go
+++ b/mvc/controller.go
@@ -396,8 +396,6 @@ func (c *ControllerActivator) attachInjector() {
if c.injector == nil {
c.injector = di.MakeStructInjector(
di.ValueOf(c.Value),
- di.DefaultHijacker,
- di.DefaultTypeChecker,
c.sorter,
di.Values(c.dependencies).CloneWithFieldsOf(c.Value)...,
)
@@ -426,7 +424,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
// fmt.Printf("for %s | values: %s\n", funcName, funcDependencies)
funcInjector := di.Func(m.Func, funcDependencies...)
- funcInjector.ErrorHandler(c.errorHandler)
+ funcInjector.ErrorHandler = c.errorHandler
// fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length)
if funcInjector.Has {
@@ -492,10 +490,6 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
return // stop as soon as possible, although it would stop later on if `ctx.StopExecution` called.
}
- // for idxx, inn := range in {
- // println("controller.go: execution: in.Value = "+inn.String()+" and in.Type = "+inn.Type().Kind().String()+" of index: ", idxx)
- // }
-
hero.DispatchFuncResult(ctx, errorHandler, call(in))
return
}
diff --git a/mvc/mvc.go b/mvc/mvc.go
index fd1121ba5..cf2bf92eb 100644
--- a/mvc/mvc.go
+++ b/mvc/mvc.go
@@ -104,6 +104,12 @@ func (app *Application) Configure(configurators ...func(*Application)) *Applicat
return app
}
+// AutoBinding used to be registered as dependency to try to automatically
+// map and bind the inputs that are not already binded with a dependency.
+//
+// A shortcut of `hero.AutoBinding`. Read more at: `hero#DefaultFallbackBinder`.
+var AutoBinding = hero.AutoBinding
+
// Register appends one or more values as dependencies.
// The value can be a single struct value-instance or a function
// which has one input and one output, the input should be