mirror of
https://github.com/kataras/iris.git
synced 2026-01-10 13:35:59 +00:00
fix https://github.com/kataras/iris/issues/1450 and continue on implementing 1449
Former-commit-id: 617f64d061e88f050a443ea1751fa244164656c5
This commit is contained in:
26
hero/di.go
26
hero/di.go
@@ -3,22 +3,12 @@ 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 IsExpectingStore(fieldOrFuncInput) {
|
||||
// return &di.BindObject{
|
||||
// Type: memstoreTyp,
|
||||
// BindType: di.Dynamic,
|
||||
// ReturnValue: func(ctxValue []reflect.Value) reflect.Value {
|
||||
// // return ctxValue[0].MethodByName("Params").Call(di.EmptyIn)[0]
|
||||
// return ctxValue[0].MethodByName("Params").Call(di.EmptyIn)[0].Field(0) // the Params' memstore.Store.
|
||||
// },
|
||||
// }, true
|
||||
// }
|
||||
|
||||
if !IsContext(fieldOrFuncInput) {
|
||||
return nil, false
|
||||
}
|
||||
@@ -29,8 +19,8 @@ func init() {
|
||||
return &di.BindObject{
|
||||
Type: contextTyp,
|
||||
BindType: di.Dynamic,
|
||||
ReturnValue: func(ctxValue []reflect.Value) reflect.Value {
|
||||
return ctxValue[0]
|
||||
ReturnValue: func(ctx context.Context) reflect.Value {
|
||||
return ctx.ReflectValue()[0]
|
||||
},
|
||||
}, true
|
||||
}
|
||||
@@ -40,4 +30,14 @@ func init() {
|
||||
// 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()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// Package di provides dependency injection for the Iris Hero and Iris MVC new features.
|
||||
// It's used internally by "hero" and "mvc" packages directly.
|
||||
// It's used internally by "hero" and "mvc" packages.
|
||||
package di
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
type (
|
||||
// Hijacker is a type which is used to catch fields or function's input argument
|
||||
@@ -11,13 +15,33 @@ type (
|
||||
// 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.
|
||||
//
|
||||
// Handles non-nil errors return from a hero handler or a controller's method (see `DispatchFuncResult`)
|
||||
// and (from v12.1.8) the error may return from a request-scoped dynamic dependency (see `MakeReturnValue`).
|
||||
ErrorHandler interface {
|
||||
HandleError(ctx context.Context, err error)
|
||||
}
|
||||
|
||||
// ErrorHandlerFunc implements the `ErrorHandler`.
|
||||
// It describes the type defnition for an error handler.
|
||||
ErrorHandlerFunc func(ctx context.Context, err error)
|
||||
)
|
||||
|
||||
// HandleError fires when the `DispatchFuncResult` or `MakereturnValue` return a non-nil error.
|
||||
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
|
||||
)
|
||||
|
||||
// Struct is being used to return a new injector based on
|
||||
@@ -26,7 +50,7 @@ var (
|
||||
// with the injector's `Inject` and `InjectElem` methods.
|
||||
func Struct(s interface{}, values ...reflect.Value) *StructInjector {
|
||||
if s == nil {
|
||||
return &StructInjector{Has: false}
|
||||
return &StructInjector{}
|
||||
}
|
||||
|
||||
return MakeStructInjector(
|
||||
@@ -45,13 +69,14 @@ func Struct(s interface{}, values ...reflect.Value) *StructInjector {
|
||||
// with the injector's `Inject` method.
|
||||
func Func(fn interface{}, values ...reflect.Value) *FuncInjector {
|
||||
if fn == nil {
|
||||
return &FuncInjector{Has: false}
|
||||
return &FuncInjector{}
|
||||
}
|
||||
|
||||
return MakeFuncInjector(
|
||||
ValueOf(fn),
|
||||
DefaultHijacker,
|
||||
DefaultTypeChecker,
|
||||
DefaultErrorHandler,
|
||||
values...,
|
||||
)
|
||||
}
|
||||
@@ -63,9 +88,10 @@ func Func(fn interface{}, values ...reflect.Value) *FuncInjector {
|
||||
type D struct {
|
||||
Values
|
||||
|
||||
hijacker Hijacker
|
||||
goodFunc TypeChecker
|
||||
sorter Sorter
|
||||
hijacker Hijacker
|
||||
goodFunc TypeChecker
|
||||
errorHandler ErrorHandler
|
||||
sorter Sorter
|
||||
}
|
||||
|
||||
// New creates and returns a new Dependency Injection container.
|
||||
@@ -87,6 +113,13 @@ func (d *D) GoodFunc(fn TypeChecker) *D {
|
||||
return d
|
||||
}
|
||||
|
||||
// ErrorHandler adds a handler which will be fired when a handler's second output argument is error and it's not nil
|
||||
// or when a request-scoped dynamic function dependency's second output argument is error and it's not nil.
|
||||
func (d *D) ErrorHandler(errorHandler ErrorHandler) *D {
|
||||
d.errorHandler = errorHandler
|
||||
return d
|
||||
}
|
||||
|
||||
// Sort sets the fields and valid bindable values sorter for struct injection.
|
||||
func (d *D) Sort(with Sorter) *D {
|
||||
d.sorter = with
|
||||
@@ -97,10 +130,11 @@ 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,
|
||||
sorter: d.sorter,
|
||||
Values: d.Values.Clone(),
|
||||
hijacker: d.hijacker,
|
||||
goodFunc: d.goodFunc,
|
||||
errorHandler: d.errorHandler,
|
||||
sorter: d.sorter,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +170,7 @@ func (d *D) Func(fn interface{}) *FuncInjector {
|
||||
ValueOf(fn),
|
||||
d.hijacker,
|
||||
d.goodFunc,
|
||||
d.errorHandler,
|
||||
d.Values...,
|
||||
)
|
||||
).ErrorHandler(d.errorHandler)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package di
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -16,9 +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
|
||||
fn reflect.Value
|
||||
typ reflect.Type
|
||||
goodFunc TypeChecker
|
||||
errorHandler ErrorHandler
|
||||
|
||||
inputs []*targetFuncInput
|
||||
// Length is the number of the valid, final binded input arguments.
|
||||
@@ -32,13 +35,15 @@ type (
|
||||
)
|
||||
|
||||
type missingInput struct {
|
||||
index int // the function's input argument's index.
|
||||
found bool
|
||||
index int // the function's input argument's index.
|
||||
found bool
|
||||
remaining Values
|
||||
}
|
||||
|
||||
func (s *FuncInjector) miss(index int) {
|
||||
func (s *FuncInjector) miss(index int, remaining Values) {
|
||||
s.lost = append(s.lost, &missingInput{
|
||||
index: index,
|
||||
index: index,
|
||||
remaining: remaining,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -46,12 +51,13 @@ func (s *FuncInjector) miss(index int) {
|
||||
// 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, values ...reflect.Value) *FuncInjector {
|
||||
func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, errorHandler ErrorHandler, values ...reflect.Value) *FuncInjector {
|
||||
typ := IndirectType(fn.Type())
|
||||
s := &FuncInjector{
|
||||
fn: fn,
|
||||
typ: typ,
|
||||
goodFunc: goodFunc,
|
||||
fn: fn,
|
||||
typ: typ,
|
||||
goodFunc: goodFunc,
|
||||
errorHandler: errorHandler,
|
||||
}
|
||||
|
||||
if !IsFunc(typ) {
|
||||
@@ -100,7 +106,7 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, v
|
||||
// but before this let's make a list of failed
|
||||
// inputs, so they can be used for a re-try
|
||||
// with different set of binding "values".
|
||||
s.miss(i)
|
||||
s.miss(i, values) // send the remaining dependencies values.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -123,11 +129,28 @@ 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)
|
||||
b, err := MakeBindObject(value, s.goodFunc, 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())
|
||||
@@ -141,9 +164,15 @@ 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) (reflect.Value, bool)) bool {
|
||||
func (s *FuncInjector) Retry(retryFn func(inIndex int, inTyp reflect.Type, remainingValues Values) (reflect.Value, bool)) bool {
|
||||
for _, missing := range s.lost {
|
||||
if missing.found {
|
||||
continue
|
||||
@@ -152,7 +181,7 @@ func (s *FuncInjector) Retry(retryFn func(inIndex int, inTyp reflect.Type) (refl
|
||||
invalidIndex := missing.index
|
||||
|
||||
inTyp := s.typ.In(invalidIndex)
|
||||
v, ok := retryFn(invalidIndex, inTyp)
|
||||
v, ok := retryFn(invalidIndex, inTyp, missing.remaining)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -186,12 +215,13 @@ func (s *FuncInjector) String() (trace string) {
|
||||
// Inject accepts an already created slice of input arguments
|
||||
// and fills them, the "ctx" is optional and it's used
|
||||
// on the dependencies that depends on one or more input arguments, these are the "ctx".
|
||||
func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
|
||||
func (s *FuncInjector) Inject(ctx context.Context, in *[]reflect.Value) {
|
||||
args := *in
|
||||
for _, input := range s.inputs {
|
||||
input.Object.Assign(ctx, func(v reflect.Value) {
|
||||
// fmt.Printf("assign input index: %d for value: %v of type: %s\n",
|
||||
// input.InputIndex, v.String(), v.Type().Name())
|
||||
|
||||
args[input.InputIndex] = v
|
||||
})
|
||||
}
|
||||
@@ -205,8 +235,8 @@ func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
|
||||
// If the function needs a receiver, so
|
||||
// the caller should be able to in[0] = receiver before injection,
|
||||
// then the `Inject` method should be used instead.
|
||||
func (s *FuncInjector) Call(ctx ...reflect.Value) []reflect.Value {
|
||||
func (s *FuncInjector) Call(ctx context.Context) []reflect.Value {
|
||||
in := make([]reflect.Value, s.Length)
|
||||
s.Inject(&in, ctx...)
|
||||
s.Inject(ctx, &in)
|
||||
return s.fn.Call(in)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package di
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
// BindType is the type of a binded object/value, it's being used to
|
||||
@@ -35,7 +37,7 @@ type BindObject struct {
|
||||
Value reflect.Value
|
||||
|
||||
BindType BindType
|
||||
ReturnValue func([]reflect.Value) reflect.Value
|
||||
ReturnValue func(ctx context.Context) reflect.Value
|
||||
}
|
||||
|
||||
// MakeBindObject accepts any "v" value, struct, pointer or a function
|
||||
@@ -43,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) (b BindObject, err error) {
|
||||
func MakeBindObject(v reflect.Value, goodFunc TypeChecker, errorHandler ErrorHandler) (b BindObject, err error) {
|
||||
if IsFunc(v) {
|
||||
b.BindType = Dynamic
|
||||
b.ReturnValue, b.Type, err = MakeReturnValue(v, goodFunc)
|
||||
b.ReturnValue, b.Type, err = MakeReturnValue(v, goodFunc, errorHandler)
|
||||
} else {
|
||||
b.BindType = Static
|
||||
b.Type = v.Type()
|
||||
@@ -67,9 +69,9 @@ var errBad = errors.New("bad")
|
||||
// The "fn" can have the following form:
|
||||
// `func(myService) MyViewModel`.
|
||||
//
|
||||
// The return type of the "fn" should be a value instance, not a pointer, for your own protection.
|
||||
// The binder function should return only one value.
|
||||
func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker) (func([]reflect.Value) reflect.Value, reflect.Type, error) {
|
||||
// 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) {
|
||||
typ := IndirectType(fn.Type())
|
||||
|
||||
// invalid if not a func.
|
||||
@@ -93,32 +95,30 @@ func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker) (func([]reflect.Val
|
||||
firstOutTyp := typ.Out(0)
|
||||
firstZeroOutVal := reflect.New(firstOutTyp).Elem()
|
||||
|
||||
bf := func(ctxValue []reflect.Value) reflect.Value {
|
||||
results := fn.Call(ctxValue)
|
||||
bf := func(ctx context.Context) reflect.Value {
|
||||
results := fn.Call(ctx.ReflectValue())
|
||||
if n == 2 {
|
||||
// two, second is always error.
|
||||
errVal := results[1]
|
||||
if !errVal.IsNil() {
|
||||
if errorHandler != nil {
|
||||
errorHandler.HandleError(ctx, errVal.Interface().(error))
|
||||
}
|
||||
|
||||
return firstZeroOutVal
|
||||
}
|
||||
}
|
||||
|
||||
v := results[0]
|
||||
if !v.IsValid() { // check the first value, second is error.
|
||||
return firstZeroOutVal
|
||||
}
|
||||
|
||||
if n == 2 {
|
||||
// two, second is always error.
|
||||
errVal := results[1]
|
||||
if !errVal.IsNil() {
|
||||
// error is not nil, do something with it.
|
||||
if ctx, ok := ctxValue[0].Interface().(interface {
|
||||
StatusCode(int)
|
||||
WriteString(string) (int, error)
|
||||
StopExecution()
|
||||
}); ok {
|
||||
ctx.StatusCode(400)
|
||||
ctx.WriteString(errVal.Interface().(error).Error())
|
||||
ctx.StopExecution()
|
||||
}
|
||||
|
||||
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() == "<interface {} Value>" {
|
||||
// println("di/object.go: " + v.String())
|
||||
@@ -138,7 +138,7 @@ func (b *BindObject) IsAssignable(to reflect.Type) bool {
|
||||
|
||||
// Assign sets the values to a setter, "toSetter" contains the setter, so the caller
|
||||
// can use it for multiple and different structs/functions as well.
|
||||
func (b *BindObject) Assign(ctx []reflect.Value, toSetter func(reflect.Value)) {
|
||||
func (b *BindObject) Assign(ctx context.Context, toSetter func(reflect.Value)) {
|
||||
if b.BindType == Dynamic {
|
||||
toSetter(b.ReturnValue(ctx))
|
||||
return
|
||||
|
||||
@@ -138,10 +138,14 @@ func IsFunc(kindable interface {
|
||||
return kindable.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
var reflectValueType = reflect.TypeOf(reflect.Value{})
|
||||
|
||||
func equalTypes(got reflect.Type, expected reflect.Type) bool {
|
||||
if got == expected {
|
||||
return true
|
||||
}
|
||||
|
||||
// fmt.Printf("got: %s expected: %s\n", got.String(), expected.String())
|
||||
// if accepts an interface, check if the given "got" type does
|
||||
// implement this "expected" user handler's input argument.
|
||||
if expected.Kind() == reflect.Interface {
|
||||
@@ -151,10 +155,6 @@ func equalTypes(got reflect.Type, expected reflect.Type) bool {
|
||||
return got.AssignableTo(expected)
|
||||
}
|
||||
|
||||
// if got.String() == "interface {}" {
|
||||
// return true
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
// Scope is the struct injector's struct value scope/permant state.
|
||||
@@ -155,7 +157,7 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
|
||||
}
|
||||
|
||||
// the binded values to the struct's fields.
|
||||
b, err := MakeBindObject(val, goodFunc)
|
||||
b, err := MakeBindObject(val, goodFunc, nil)
|
||||
if err != nil {
|
||||
return s // if error stop here.
|
||||
}
|
||||
@@ -288,17 +290,17 @@ func (s *StructInjector) String() (trace string) {
|
||||
// Inject accepts a destination struct and any optional context value(s),
|
||||
// hero and mvc takes only one context value and this is the `context.Context`.
|
||||
// It applies the bindings to the "dest" struct. It calls the InjectElem.
|
||||
func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
|
||||
func (s *StructInjector) Inject(ctx context.Context, dest interface{}) {
|
||||
if dest == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v := IndirectValue(ValueOf(dest))
|
||||
s.InjectElem(v, ctx...)
|
||||
s.InjectElem(ctx, v)
|
||||
}
|
||||
|
||||
// InjectElem same as `Inject` but accepts a reflect.Value and bind the necessary fields directly.
|
||||
func (s *StructInjector) InjectElem(destElem reflect.Value, ctx ...reflect.Value) {
|
||||
func (s *StructInjector) InjectElem(ctx context.Context, destElem reflect.Value) {
|
||||
for _, f := range s.fields {
|
||||
f.Object.Assign(ctx, func(v reflect.Value) {
|
||||
ff := destElem.FieldByIndex(f.FieldIndex)
|
||||
|
||||
@@ -20,23 +20,6 @@ type Result interface {
|
||||
Dispatch(ctx context.Context)
|
||||
}
|
||||
|
||||
type (
|
||||
// ErrorHandler is the optional interface to handle errors per hero func,
|
||||
// see `mvc/Application#HandleError` for MVC application-level error handler registration too.
|
||||
ErrorHandler interface {
|
||||
HandleError(ctx context.Context, err error)
|
||||
}
|
||||
|
||||
// ErrorHandlerFunc implements the `ErrorHandler`.
|
||||
// It describes the type defnition for an error handler.
|
||||
ErrorHandlerFunc func(ctx context.Context, err error)
|
||||
)
|
||||
|
||||
// HandleError fires when the `DispatchFuncResult` returns a non-nil error.
|
||||
func (fn ErrorHandlerFunc) HandleError(ctx context.Context, err error) {
|
||||
fn(ctx, err)
|
||||
}
|
||||
|
||||
var defaultFailureResponse = Response{Code: DefaultErrStatusCode}
|
||||
|
||||
// Try will check if "fn" ran without any panics,
|
||||
@@ -180,7 +163,7 @@ func DispatchCommon(ctx context.Context,
|
||||
// Result or (Result, error) and so on...
|
||||
//
|
||||
// where Get is an HTTP METHOD.
|
||||
func DispatchFuncResult(ctx context.Context, errorHandler ErrorHandler, values []reflect.Value) {
|
||||
func DispatchFuncResult(ctx context.Context, errorHandler di.ErrorHandler, values []reflect.Value) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -314,7 +297,7 @@ func DispatchFuncResult(ctx context.Context, errorHandler ErrorHandler, values [
|
||||
|
||||
if errorHandler != nil {
|
||||
errorHandler.HandleError(ctx, value)
|
||||
break
|
||||
return
|
||||
}
|
||||
|
||||
err = value
|
||||
|
||||
@@ -18,6 +18,14 @@ 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
|
||||
}
|
||||
|
||||
// checks if "handler" is context.Handler: func(context.Context).
|
||||
func isContextHandler(handler interface{}) (context.Handler, bool) {
|
||||
h, is := handler.(context.Handler)
|
||||
@@ -42,7 +50,7 @@ func validateHandler(handler interface{}) error {
|
||||
// custom structs, Result(View | Response) and anything that you can imagine,
|
||||
// and returns a low-level `context/iris.Handler` which can be used anywhere in the Iris Application,
|
||||
// as middleware or as simple route handler or party handler or subdomain handler-router.
|
||||
func makeHandler(handler interface{}, values ...reflect.Value) (context.Handler, error) {
|
||||
func makeHandler(handler interface{}, errorHandler di.ErrorHandler, values ...reflect.Value) (context.Handler, error) {
|
||||
if err := validateHandler(handler); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -64,6 +72,8 @@ func makeHandler(handler interface{}, values ...reflect.Value) (context.Handler,
|
||||
}
|
||||
|
||||
funcInjector := di.Func(fn, values...)
|
||||
funcInjector.ErrorHandler(errorHandler)
|
||||
|
||||
valid := funcInjector.Length == n
|
||||
|
||||
if !valid {
|
||||
@@ -73,7 +83,8 @@ func makeHandler(handler interface{}, values ...reflect.Value) (context.Handler,
|
||||
// using binders for path parameters: string, int, int64, uint8, uint64, bool and so on.
|
||||
// We don't have access to the path, so neither to the macros here,
|
||||
// but in mvc. So we have to do it here.
|
||||
if valid = funcInjector.Retry(new(params).resolve); !valid {
|
||||
valid = funcInjector.Retry(new(params).resolve)
|
||||
if !valid {
|
||||
pc := fn.Pointer()
|
||||
fpc := runtime.FuncForPC(pc)
|
||||
callerFileName, callerLineNumber := fpc.FileLine(pc)
|
||||
@@ -89,7 +100,7 @@ func makeHandler(handler interface{}, values ...reflect.Value) (context.Handler,
|
||||
// in := make([]reflect.Value, n, n)
|
||||
// funcInjector.Inject(&in, reflect.ValueOf(ctx))
|
||||
// DispatchFuncResult(ctx, fn.Call(in))
|
||||
DispatchFuncResult(ctx, nil, funcInjector.Call(reflect.ValueOf(ctx)))
|
||||
DispatchFuncResult(ctx, nil, funcInjector.Call(ctx))
|
||||
}
|
||||
|
||||
return h, nil
|
||||
|
||||
@@ -4,6 +4,7 @@ package hero_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
@@ -14,8 +15,8 @@ import (
|
||||
|
||||
// dynamic func
|
||||
type testUserStruct struct {
|
||||
ID int64
|
||||
Username string
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
func testBinderFunc(ctx iris.Context) testUserStruct {
|
||||
@@ -126,3 +127,38 @@ func TestBindFunctionAsFunctionInputArgument(t *testing.T) {
|
||||
e.POST("/").WithFormField("username", expectedUsername).
|
||||
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)
|
||||
|
||||
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{}
|
||||
})
|
||||
postHandler := h.Handler(func(input testUserStruct) string {
|
||||
return input.Username
|
||||
})
|
||||
|
||||
app := iris.New()
|
||||
app.Post("/", postHandler)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().Equal("makis")
|
||||
}
|
||||
|
||||
18
hero/hero.go
18
hero/hero.go
@@ -1,10 +1,10 @@
|
||||
package hero
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/hero/di"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
// def is the default hero value which can be used for dependencies share.
|
||||
@@ -22,7 +22,8 @@ var def = New()
|
||||
//
|
||||
// For a more high-level structure please take a look at the "mvc.go#Application".
|
||||
type Hero struct {
|
||||
values di.Values
|
||||
values di.Values
|
||||
errorHandler di.ErrorHandler
|
||||
}
|
||||
|
||||
// New returns a new Hero, a container for dependencies and a factory
|
||||
@@ -30,7 +31,8 @@ type Hero struct {
|
||||
// Please take a look at the structure's documentation for more information.
|
||||
func New() *Hero {
|
||||
return &Hero{
|
||||
values: di.NewValues(),
|
||||
values: di.NewValues(),
|
||||
errorHandler: di.DefaultErrorHandler,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +85,14 @@ func (h *Hero) Clone() *Hero {
|
||||
return child
|
||||
}
|
||||
|
||||
// ErrorHandler sets a handler for this hero instance
|
||||
// which will be fired when a handler's second output argument is error and it's not nil
|
||||
// or when a request-scoped dynamic function dependency's second output argument is error and it's not nil.
|
||||
func (h *Hero) ErrorHandler(errorHandler func(ctx context.Context, err error)) *Hero {
|
||||
h.errorHandler = di.ErrorHandlerFunc(errorHandler)
|
||||
return h
|
||||
}
|
||||
|
||||
// Handler accepts a "handler" function which can accept any input arguments that match
|
||||
// with the Hero's `Dependencies` and any output result; like string, int (string,int),
|
||||
// custom structs, Result(View | Response) and anything you can imagine.
|
||||
@@ -98,7 +108,7 @@ func Handler(handler interface{}) context.Handler {
|
||||
// It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application,
|
||||
// as middleware or as simple route handler or subdomain's handler.
|
||||
func (h *Hero) Handler(fn interface{}) context.Handler {
|
||||
handler, err := makeHandler(fn, h.values.Clone()...)
|
||||
handler, err := makeHandler(fn, h.errorHandler, h.values.Clone()...)
|
||||
if err != nil {
|
||||
golog.Errorf("hero handler: %v", err)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/hero/di"
|
||||
)
|
||||
|
||||
// weak because we don't have access to the path, neither
|
||||
@@ -17,7 +18,7 @@ type params struct {
|
||||
next int
|
||||
}
|
||||
|
||||
func (p *params) resolve(index int, typ reflect.Type) (reflect.Value, bool) {
|
||||
func (p *params) resolve(index int, typ reflect.Type, _ di.Values) (reflect.Value, bool) {
|
||||
currentParamIndex := p.next
|
||||
v, ok := context.ParamResolverByTypeAndIndex(typ, currentParamIndex)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user