mirror of
https://github.com/kataras/iris.git
synced 2025-03-14 02:56:26 +01:00
Update to version 12.1.8 - Read HISTORY.md
Former-commit-id: d3d30cb15537146e3071731be9d674a5cb59de97
This commit is contained in:
parent
899aec8b19
commit
08403f0317
14
HISTORY.md
14
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<!-- # News
|
||||
# News
|
||||
|
||||
 Iris version **12.1.7** has been [released](HISTORY.md#mo-10-february-2020--v1217)!
|
||||
 Iris version **12.1.8** has been [released](HISTORY.md#su-16-february-2020--v1218)!
|
||||
|
||||
 The official [Iris Command Line Interface](https://github.com/kataras/iris-cli) will soon be near you in 2020!
|
||||
|
||||
 Support your favorite web framework through [Github Sponsors Program](https://github.com/sponsors/kataras)! -->
|
||||
 Support your favorite web framework through [Github Sponsors Program](https://github.com/sponsors/kataras)!
|
||||
|
||||
# Iris Web Framework <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_KO.md"><img width="20px" src="https://iris-go.com/images/flag-south-korea.svg" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg" /></a>
|
||||
|
||||
[](https://travis-ci.org/kataras/iris) [](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[](https://goreportcard.com/report/github.com/kataras/iris)--><!--[](https://godoc.org/github.com/kataras/iris)--> [](https://github.com/kataras/iris/tree/master/_examples) [](https://gitter.im/iris_go/community)<!--[](https://www.paypal.me/kataras)--><!-- [](https://github.com/kataras/iris/releases) -->
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
12.1.7:https://github.com/kataras/iris/releases/tag/v12.1.7
|
||||
12.1.8:https://github.com/kataras/iris/releases/tag/v12.1.8
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{}},
|
||||
}},
|
||||
}}
|
||||
|
||||
|
|
2
doc.go
2
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
|
||||
|
||||
|
|
43
hero/di.go
43
hero/di.go
|
@ -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()
|
||||
})
|
||||
}
|
135
hero/di/di.go
135
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{}
|
||||
return &D{
|
||||
errorHandler: DefaultErrorHandler,
|
||||
fallbackBinder: DefaultFallbackBinder,
|
||||
}
|
||||
}
|
||||
|
||||
// Hijack sets a hijacker function, read the `Hijacker` type for more explanation.
|
||||
func (d *D) Hijack(fn Hijacker) *D {
|
||||
d.hijacker = fn
|
||||
return d
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
113
hero/di/func.go
113
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 {
|
||||
|
|
|
@ -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() == "<interface {} Value>" {
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
2
iris.go
2
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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user