1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-17 18:07:01 +00:00

❤️ awesome and unique features for end-developers are coming...

total refactor of the hero and mvc packages, see README#Next (it's not completed yet)


Former-commit-id: b85ae99cbfe5965ba919c1e15cf4989e787982c0
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-02-29 14:18:15 +02:00
parent 027eb5d6da
commit 5fc24812bc
54 changed files with 2916 additions and 2184 deletions

View File

@@ -8,10 +8,6 @@ import (
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/router"
"github.com/kataras/iris/v12/hero"
"github.com/kataras/iris/v12/hero/di"
"github.com/kataras/iris/v12/macro"
"github.com/kataras/golog"
)
// BaseController is the optional controller interface, if it's
@@ -41,7 +37,7 @@ type shared interface {
// it's called once per application.
type BeforeActivation interface {
shared
Dependencies() *di.Values
Dependencies() *hero.Container
}
// AfterActivation is being used as the only one input argument of a
@@ -54,8 +50,8 @@ type BeforeActivation interface {
// it's called once per application.
type AfterActivation interface {
shared
DependenciesReadOnly() ValuesReadOnly
Singleton() bool
DependenciesReadOnly() []*hero.Dependency
}
var (
@@ -66,12 +62,9 @@ var (
// ControllerActivator returns a new controller type info description.
// Its functionality can be overridden by the end-dev.
type ControllerActivator struct {
// the router is used on the `Activate` and can be used by end-dev on the `BeforeActivation`
// to register any custom controller's methods as handlers.
router router.Party
app *Application
macros macro.Macros
tmplParamStartIndex int
injector *hero.Struct
// initRef BaseController // the BaseController as it's passed from the end-dev.
Value reflect.Value // the BaseController's Value.
@@ -85,17 +78,6 @@ type ControllerActivator struct {
// `GetRoute/GetRoutes(functionName)`.
routes map[string][]*router.Route
// the bindings that comes from the Engine and the controller's filled fields if any.
// Can be bind-ed to the the new controller's fields and method that is fired
// on incoming requests.
dependencies di.Values
sorter di.Sorter
errorHandler di.ErrorHandler
// initialized on the first `Handle` or immediately when "servesWebsocket" is true.
injector *di.StructInjector
// true if this controller listens and serves to websocket events.
servesWebsocket bool
}
@@ -103,7 +85,7 @@ type ControllerActivator struct {
// NameOf returns the package name + the struct type's name,
// it's used to take the full name of an Controller, the `ControllerActivator#Name`.
func NameOf(v interface{}) string {
elemTyp := di.IndirectType(di.ValueOf(v).Type())
elemTyp := indirectType(reflect.ValueOf(v).Type())
typName := elemTyp.Name()
pkgPath := elemTyp.PkgPath()
@@ -112,16 +94,15 @@ func NameOf(v interface{}) string {
return fullname
}
func newControllerActivator(router router.Party, controller interface{}, dependencies []reflect.Value, sorter di.Sorter, errorHandler di.ErrorHandler) *ControllerActivator {
func newControllerActivator(app *Application, controller interface{}) *ControllerActivator {
typ := reflect.TypeOf(controller)
c := &ControllerActivator{
// give access to the Router to the end-devs if they need it for some reason,
// i.e register done handlers.
router: router,
macros: *router.Macros(),
Value: reflect.ValueOf(controller),
Type: typ,
app: app,
Value: reflect.ValueOf(controller),
Type: typ,
// the full name of the controller: its type including the package path.
fullName: NameOf(controller),
// set some methods that end-dev cann't use accidentally
@@ -131,14 +112,8 @@ func newControllerActivator(router router.Party, controller interface{}, depende
// if a new method is registered via `Handle` its function name
// is also appended to that slice.
routes: whatReservedMethods(typ),
// CloneWithFieldsOf: include the manual fill-ed controller struct's fields to the dependencies.
dependencies: di.Values(dependencies).CloneWithFieldsOf(controller),
sorter: sorter,
errorHandler: errorHandler,
}
fpath, _ := macro.Parse(c.router.GetRelPath(), c.macros)
c.tmplParamStartIndex = len(fpath.Params)
return c
}
@@ -158,39 +133,6 @@ func whatReservedMethods(typ reflect.Type) map[string][]*router.Route {
return routes
}
func (c *ControllerActivator) markAsWebsocket() {
c.servesWebsocket = true
c.attachInjector()
}
// Dependencies returns the write and read access of the dependencies that are
// came from the parent MVC Application, with this you can customize
// the dependencies per controller, used at the `BeforeActivation`.
func (c *ControllerActivator) Dependencies() *di.Values {
return &c.dependencies
}
// ValuesReadOnly returns the read-only access type of the controller's dependencies.
// Used at `AfterActivation`.
type ValuesReadOnly interface {
// Has returns true if a binder responsible to
// bind and return a type of "typ" is already registered to this controller.
Has(value interface{}) bool
// Len returns the length of the values.
Len() int
// Clone returns a copy of the current values.
Clone() di.Values
// CloneWithFieldsOf will return a copy of the current values
// plus the "s" struct's fields that are filled(non-zero) by the caller.
CloneWithFieldsOf(s interface{}) di.Values
}
// DependenciesReadOnly returns the read-only access type of the controller's dependencies.
// Used at `AfterActivation`.
func (c *ControllerActivator) DependenciesReadOnly() ValuesReadOnly {
return c.dependencies
}
// Name returns the full name of the controller, its package name + the type name.
// Can used at both `BeforeActivation` and `AfterActivation`.
func (c *ControllerActivator) Name() string {
@@ -206,7 +148,7 @@ func (c *ControllerActivator) Name() string {
//
// Can used at both `BeforeActivation` and `AfterActivation`.
func (c *ControllerActivator) Router() router.Party {
return c.router
return c.app.Router
}
// GetRoute returns the first registered route based on the controller's method name.
@@ -252,9 +194,23 @@ func (c *ControllerActivator) GetRoutes(methodName string) []*router.Route {
// any unexported fields and all fields are services-like, static.
func (c *ControllerActivator) Singleton() bool {
if c.injector == nil {
panic("MVC: Singleton used on an invalid state the API gives access to it only `AfterActivation`, report this as bug")
panic("MVC: Singleton called from wrong state the API gives access to it only `AfterActivation`, report this as bug")
}
return c.injector.Scope == di.Singleton
return c.injector.Singleton
}
// DependenciesReadOnly returns a list of dependencies, including the controller's one.
func (c *ControllerActivator) DependenciesReadOnly() []*hero.Dependency {
if c.injector == nil {
panic("MVC: DependenciesReadOnly called from wrong state the API gives access to it only `AfterActivation`, report this as bug")
}
return c.injector.Container.Dependencies
}
// Dependencies returns a value which can manage the controller's dependencies.
func (c *ControllerActivator) Dependencies() *hero.Container {
return c.app.container
}
// checks if a method is already registered.
@@ -268,12 +224,19 @@ func (c *ControllerActivator) isReservedMethod(name string) bool {
return false
}
func (c *ControllerActivator) activate() {
c.parseMethods()
func (c *ControllerActivator) attachInjector() {
if c.injector == nil {
c.injector = c.app.container.Struct(c.Value)
}
}
func (c *ControllerActivator) addErr(err error) bool {
return c.router.GetReporter().Err(err) != nil
func (c *ControllerActivator) markAsWebsocket() {
c.servesWebsocket = true
c.attachInjector()
}
func (c *ControllerActivator) activate() {
c.parseMethods()
}
// register all available, exported methods to handlers if possible.
@@ -286,7 +249,7 @@ func (c *ControllerActivator) parseMethods() {
}
func (c *ControllerActivator) parseMethod(m reflect.Method) {
httpMethod, httpPath, err := parseMethod(*c.router.Macros(), m, c.isReservedMethod)
httpMethod, httpPath, err := parseMethod(c.app.Router.Macros(), m, c.isReservedMethod)
if err != nil {
if err != errSkip {
c.addErr(fmt.Errorf("MVC: fail to parse the route path and HTTP method for '%s.%s': %v", c.fullName, m.Name, err))
@@ -298,6 +261,10 @@ func (c *ControllerActivator) parseMethod(m reflect.Method) {
c.Handle(httpMethod, httpPath, m.Name)
}
func (c *ControllerActivator) addErr(err error) bool {
return c.app.Router.GetReporter().Err(err) != nil
}
// Handle registers a route based on a http method, the route's path
// and a function name that belongs to the controller, it accepts
// a forth, optionally, variadic parameter which is the before handlers.
@@ -336,35 +303,10 @@ func (c *ControllerActivator) handleMany(method, path, funcName string, override
return nil
}
// get the method from the controller type.
m, ok := c.Type.MethodByName(funcName)
if !ok {
c.addErr(fmt.Errorf("MVC: function '%s' doesn't exist inside the '%s' controller",
funcName, c.fullName))
return nil
}
// parse a route template which contains the parameters organised.
tmpl, err := macro.Parse(path, c.macros)
if err != nil {
c.addErr(fmt.Errorf("MVC: fail to parse the path for '%s.%s': %v", c.fullName, funcName, err))
return nil
}
// get the function's input.
funcIn := getInputArgsFromFunc(m.Type)
// get the path parameters bindings from the template,
// use the function's input except the receiver which is the
// end-dev's controller pointer.
pathParams := getPathParamsForInput(c.tmplParamStartIndex, tmpl.Params, funcIn[1:]...)
// get the function's input arguments' bindings.
funcDependencies := c.dependencies.Clone()
funcDependencies.AddValues(pathParams...)
handler := c.handlerOf(m, funcDependencies)
handler := c.handlerOf(funcName)
// register the handler now.
routes := c.router.HandleMany(method, path, append(middleware, handler)...)
routes := c.app.Router.HandleMany(method, path, append(middleware, handler)...)
if routes == nil {
c.addErr(fmt.Errorf("MVC: unable to register a route for the path for '%s.%s'", c.fullName, funcName))
return nil
@@ -390,79 +332,21 @@ func (c *ControllerActivator) handleMany(method, path, funcName string, override
return routes
}
var emptyIn = []reflect.Value{}
func (c *ControllerActivator) attachInjector() {
if c.injector == nil {
c.injector = di.MakeStructInjector(
di.ValueOf(c.Value),
c.sorter,
di.Values(c.dependencies).CloneWithFieldsOf(c.Value)...,
)
// c.injector = di.Struct(c.Value, c.dependencies...)
if !c.servesWebsocket {
golog.Debugf("MVC Controller [%s] [Scope=%s]", c.fullName, c.injector.Scope)
} else {
golog.Debugf("MVC Websocket Controller [%s]", c.fullName)
}
if c.injector.Has {
golog.Debugf("Dependencies:\n%s", c.injector.String())
}
}
}
func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []reflect.Value) context.Handler {
// Remember:
// The `Handle->handlerOf` can be called from `BeforeActivation` event
// then, the c.injector is nil because
// we may not have the dependencies bind-ed yet.
// To solve this we're doing a check on the FIRST `Handle`,
// if c.injector is nil, then set it with the current bindings,
// these bindings can change after, so first add dependencies and after register routes.
func (c *ControllerActivator) handlerOf(methodName string) context.Handler {
c.attachInjector()
// fmt.Printf("for %s | values: %s\n", funcName, funcDependencies)
funcInjector := di.Func(m.Func, funcDependencies...)
funcInjector.ErrorHandler = c.errorHandler
handler := c.injector.MethodHandler(methodName)
// fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length)
if funcInjector.Has {
golog.Debugf("MVC dependencies of method '%s.%s':\n%s", c.fullName, m.Name, funcInjector.String())
}
var (
implementsBase = isBaseController(c.Type)
implementsErrorHandler = isErrorHandler(c.Type)
hasBindableFields = c.injector.CanInject
hasBindableFuncInputs = funcInjector.Has
funcHasErrorOut = hasErrorOutArgs(m)
call = m.Func.Call
)
if !implementsBase && !hasBindableFields && !hasBindableFuncInputs && !implementsErrorHandler {
if isBaseController(c.Type) {
return func(ctx context.Context) {
hero.DispatchFuncResult(ctx, c.errorHandler, call(c.injector.AcquireSlice()))
}
}
ctrl, err := c.injector.Acquire(ctx)
if err != nil {
if err != hero.ErrStopExecution {
c.injector.Container.GetErrorHandler(ctx).HandleError(ctx, err)
}
return
}
n := m.Type.NumIn()
return func(ctx context.Context) {
var (
ctrl = c.injector.Acquire()
errorHandler = c.errorHandler
)
// inject struct fields first before the BeginRequest and EndRequest, if any,
// in order to be able to have access there.
if hasBindableFields {
c.injector.InjectElem(ctx, ctrl.Elem())
}
// check if has BeginRequest & EndRequest, before try to bind the method's inputs.
if implementsBase {
// the Interface(). is faster than MethodByName or pre-selected methods.
b := ctrl.Interface().(BaseController)
// init the request.
b.BeginRequest(ctx)
@@ -472,28 +356,11 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
return
}
defer b.EndRequest(ctx)
handler(ctx)
b.EndRequest(ctx)
}
if funcHasErrorOut && implementsErrorHandler {
errorHandler = ctrl.Interface().(di.ErrorHandler)
}
if hasBindableFuncInputs {
// means that ctxValue is not initialized before by the controller's struct injector.
in := make([]reflect.Value, n)
in[0] = ctrl
funcInjector.Inject(ctx, &in)
if ctx.IsStopped() {
return // stop as soon as possible, although it would stop later on if `ctx.StopExecution` called.
}
hero.DispatchFuncResult(ctx, errorHandler, call(in))
return
}
hero.DispatchFuncResult(ctx, errorHandler, ctrl.Method(m.Index).Call(emptyIn))
}
return handler
}

View File

@@ -12,24 +12,24 @@ import (
// service
type (
// these TestService and TestServiceImpl could be in lowercase, unexported
// these testService and testServiceImpl could be in lowercase, unexported
// but the `Say` method should be exported however we have those exported
// because of the controller handler test.
TestService interface {
testService interface {
Say(string) string
}
TestServiceImpl struct {
testServiceImpl struct {
prefix string
}
)
func (s *TestServiceImpl) Say(message string) string {
func (s *testServiceImpl) Say(message string) string {
return s.prefix + " " + message
}
type testControllerHandle struct {
Ctx context.Context
Service TestService
Service testService
reqField string
}
@@ -41,12 +41,7 @@ func (c *testControllerHandle) BeforeActivation(b BeforeActivation) {
b.Handle("GET", "/hiparam/{ps:string}", "HiParamBy")
b.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy")
b.HandleMany("GET", "/custom/{ps:string} /custom2/{ps:string}", "CustomWithParameter")
// if dynamic path exist
// then longest path should be registered first
// and the controller's method if wants to add path parameters
// dependency injection then they should accept the longest path parameters.
// See `testControllerHandle.CustomWithParameters`.
b.HandleMany("GET", "/custom3/{ps:string}/{pssecond:string} /custom3/{ps:string}", "CustomWithParameters")
b.HandleMany("GET", "/custom3/{ps:string}/{pssecond:string}", "CustomWithParameters")
}
// test `GetRoute` for custom routes.
@@ -109,8 +104,6 @@ func (c *testControllerHandle) CustomWithParameter(param1 string) string {
}
func (c *testControllerHandle) CustomWithParameters(param1, param2 string) string {
// it returns empty string for requested path: /custom3/value1,
// see BeforeActivation.
return param1 + param2
}
@@ -124,7 +117,7 @@ func (c *testSmallController) GetHiParamEmptyInputWithCtxBy(ctx context.Context,
func TestControllerHandle(t *testing.T) {
app := iris.New()
m := New(app)
m.Register(&TestServiceImpl{prefix: "service:"})
m.Register(&testServiceImpl{prefix: "service:"})
m.Handle(new(testControllerHandle))
m.Handle(new(testSmallController))
@@ -160,8 +153,7 @@ func TestControllerHandle(t *testing.T) {
Body().Equal("value2")
e.GET("/custom3/value1/value2").Expect().Status(httptest.StatusOK).
Body().Equal("value1value2")
e.GET("/custom3/value1").Expect().Status(httptest.StatusOK).
Body().Equal("value1")
e.GET("/custom3/value1").Expect().Status(httptest.StatusNotFound)
e.GET("/hi/param/empty/input/with/ctx/value").Expect().Status(httptest.StatusOK).
Body().Equal("empty in but served with ctx.Params.Get('param2')= value == id == value")

View File

@@ -94,10 +94,10 @@ func genParamKey(argIdx int) string {
type methodParser struct {
lexer *methodLexer
fn reflect.Method
macros macro.Macros
macros *macro.Macros
}
func parseMethod(macros macro.Macros, fn reflect.Method, skipper func(string) bool) (method, path string, err error) {
func parseMethod(macros *macro.Macros, fn reflect.Method, skipper func(string) bool) (method, path string, err error) {
if skipper(fn.Name) {
return "", "", errSkip
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/router"
"github.com/kataras/iris/v12/hero"
"github.com/kataras/iris/v12/httptest"
. "github.com/kataras/iris/v12/mvc"
@@ -281,7 +282,7 @@ type testControllerBindDeep struct {
}
func (t *testControllerBindDeep) BeforeActivation(b BeforeActivation) {
b.Dependencies().Add(func(ctx iris.Context) (v testCustomStruct, err error) {
b.Dependencies().Register(func(ctx iris.Context) (v testCustomStruct, err error) {
err = ctx.ReadJSON(&v)
return
})
@@ -467,7 +468,7 @@ type testControllerActivateListener struct {
}
func (c *testControllerActivateListener) BeforeActivation(b BeforeActivation) {
b.Dependencies().AddOnce(&testBindType{title: "default title"})
b.Dependencies().Register(&testBindType{title: "overrides the dependency but not the field"}) // overrides the `Register` previous calls.
}
func (c *testControllerActivateListener) Get() string {
@@ -485,17 +486,17 @@ func TestControllerActivateListener(t *testing.T) {
// or
m.Party("/manual2").Handle(&testControllerActivateListener{
TitlePointer: &testBindType{
title: "my title",
title: "my manual title",
},
})
e := httptest.New(t, app)
e.GET("/").Expect().Status(iris.StatusOK).
Body().Equal("default title")
Body().Equal("overrides the dependency but not the field")
e.GET("/manual").Expect().Status(iris.StatusOK).
Body().Equal("my title")
Body().Equal("overrides the dependency but not the field")
e.GET("/manual2").Expect().Status(iris.StatusOK).
Body().Equal("my title")
Body().Equal("my manual title")
}
type testControllerNotCreateNewDueManuallySettingAllFields struct {
@@ -505,13 +506,12 @@ type testControllerNotCreateNewDueManuallySettingAllFields struct {
}
func (c *testControllerNotCreateNewDueManuallySettingAllFields) AfterActivation(a AfterActivation) {
if n := a.DependenciesReadOnly().Len(); n != 2 {
c.T.Fatalf(`expecting 2 dependency, the 'T' and the 'TitlePointer' that we manually insert
and the fields total length is 2 so it will not create a new controller on each request
however the dependencies are available here
although the struct injector is being ignored when
creating the controller's handlers because we set it to invalidate state at "newControllerActivator"
-- got dependencies length: %d`, n)
if n := len(a.DependenciesReadOnly()) - len(hero.BuiltinDependencies); n != 1 {
c.T.Fatalf(`expecting 1 dependency;
- the 'T' and the 'TitlePointer' are manually binded (nonzero fields on initilization)
- controller has no more than these two fields, it's a singleton
- however, the dependencies length here should be 1 because the injector's options handler dependencies contains the controller's value dependency itself
-- got dependencies length: %d`, n)
}
if !a.Singleton() {

View File

@@ -1,5 +1,3 @@
// +build go1.9
package mvc
import "github.com/kataras/iris/v12/hero"

View File

@@ -7,26 +7,11 @@ import (
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/router"
"github.com/kataras/iris/v12/hero"
"github.com/kataras/iris/v12/hero/di"
"github.com/kataras/iris/v12/websocket"
"github.com/kataras/golog"
)
// HeroDependencies let you share bindable dependencies between
// package-level hero's registered dependencies and all MVC instances that comes later.
//
// `hero.Register(...)`
// `myMVC := mvc.New(app.Party(...))`
// the "myMVC" registers the dependencies provided by the `hero.Register` func
// automatically.
//
// Set it to false to disable that behavior, you have to use the `mvc#Register`
// even if you had register dependencies with the `hero` package.
//
// Defaults to true.
var HeroDependencies = true
// Application is the high-level component of the "mvc" package.
// It's the API that you will be using to register controllers among with their
// dependencies that your controllers may expecting.
@@ -39,23 +24,17 @@ var HeroDependencies = true
//
// See `mvc#New` for more.
type Application struct {
Dependencies di.Values
// Sorter is a `di.Sorter`, can be used to customize the order of controller's fields
// and their available bindable values to set.
// Sorting matters only when a field can accept more than one registered value.
// Defaults to nil; order of registration matters when more than one field can accept the same value.
Sorter di.Sorter
container *hero.Container
Router router.Party
Controllers []*ControllerActivator
websocketControllers []websocket.ConnHandler
ErrorHandler di.ErrorHandler
}
func newApp(subRouter router.Party, values di.Values) *Application {
func newApp(subRouter router.Party, container *hero.Container) *Application {
return &Application{
Router: subRouter,
Dependencies: values,
ErrorHandler: di.DefaultErrorHandler,
Router: subRouter,
container: container,
}
}
@@ -65,12 +44,7 @@ func newApp(subRouter router.Party, values di.Values) *Application {
//
// Example: `New(app.Party("/todo"))` or `New(app)` as it's the same as `New(app.Party("/"))`.
func New(party router.Party) *Application {
values := di.NewValues()
if HeroDependencies {
values = hero.Dependencies().Clone()
}
return newApp(party, values)
return newApp(party, party.GetContainer())
}
// Configure creates a new controller and configures it,
@@ -104,12 +78,6 @@ 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
@@ -117,39 +85,32 @@ var AutoBinding = hero.AutoBinding
// will be bind-ed to the controller's field, if matching or to the
// controller's methods, if matching.
//
// These dependencies "values" can be changed per-controller as well,
// These dependencies "dependencies" can be changed per-controller as well,
// via controller's `BeforeActivation` and `AfterActivation` methods,
// look the `Handle` method for more.
//
// It returns this Application.
//
// Example: `.Register(loggerService{prefix: "dev"}, func(ctx iris.Context) User {...})`.
func (app *Application) Register(values ...interface{}) *Application {
if len(values) > 0 && app.Dependencies.Len() == 0 && len(app.Controllers) > 0 {
func (app *Application) Register(dependencies ...interface{}) *Application {
if len(dependencies) > 0 && len(app.container.Dependencies) == len(hero.BuiltinDependencies) && len(app.Controllers) > 0 {
allControllerNamesSoFar := make([]string, len(app.Controllers))
for i := range app.Controllers {
allControllerNamesSoFar[i] = app.Controllers[i].Name()
}
golog.Warnf(`mvc.Application#Register called after mvc.Application#Handle.
The controllers[%s] may miss required dependencies.
Set the Logger's Level to "debug" to view the active dependencies per controller.`, strings.Join(allControllerNamesSoFar, ","))
The controllers[%s] may miss required dependencies.
Set the Logger's Level to "debug" to view the active dependencies per controller.`, strings.Join(allControllerNamesSoFar, ","))
}
app.Dependencies.Add(values...)
for _, dependency := range dependencies {
app.container.Register(dependency)
}
return app
}
// SortByNumMethods is the same as `app.Sorter = di.SortByNumMethods` which
// prioritize fields and their available values (only if more than one)
// with the highest number of methods,
// this way an empty interface{} is getting the "thinnest" available value.
func (app *Application) SortByNumMethods() *Application {
app.Sorter = di.SortByNumMethods
return app
}
// Handle serves a controller for the current mvc application's Router.
// It accept any custom struct which its functions will be transformed
// to routes.
@@ -214,12 +175,9 @@ func (app *Application) HandleWebsocket(controller interface{}) *websocket.Struc
return websocketController
}
func makeInjector(injector *di.StructInjector) websocket.StructInjector {
func makeInjector(s *hero.Struct) websocket.StructInjector {
return func(_ reflect.Type, nsConn *websocket.NSConn) reflect.Value {
v := injector.Acquire()
if injector.CanInject {
injector.InjectElem(websocket.GetContext(nsConn.Conn), v.Elem())
}
v, _ := s.Acquire(websocket.GetContext(nsConn.Conn))
return v
}
}
@@ -239,7 +197,7 @@ func (app *Application) GetNamespaces() websocket.Namespaces {
func (app *Application) handle(controller interface{}) *ControllerActivator {
// initialize the controller's activator, nothing too magical so far.
c := newControllerActivator(app.Router, controller, app.Dependencies, app.Sorter, app.ErrorHandler)
c := newControllerActivator(app, controller)
// check the controller's "BeforeActivation" or/and "AfterActivation" method(s) between the `activate`
// call, which is simply parses the controller's methods, end-dev can register custom controller's methods
@@ -265,8 +223,11 @@ func (app *Application) handle(controller interface{}) *ControllerActivator {
// HandleError registers a `hero.ErrorHandlerFunc` which will be fired when
// application's controllers' functions returns an non-nil error.
// Each controller can override it by implementing the `hero.ErrorHandler`.
func (app *Application) HandleError(errorHandler func(ctx context.Context, err error)) *Application {
app.ErrorHandler = di.ErrorHandlerFunc(errorHandler)
func (app *Application) HandleError(handler func(ctx context.Context, err error)) *Application {
errorHandler := hero.ErrorHandlerFunc(handler)
app.container.GetErrorHandler = func(context.Context) hero.ErrorHandler {
return errorHandler
}
return app
}
@@ -275,8 +236,7 @@ func (app *Application) HandleError(errorHandler func(ctx context.Context, err e
//
// Example: `.Clone(app.Party("/path")).Handle(new(TodoSubController))`.
func (app *Application) Clone(party router.Party) *Application {
cloned := newApp(party, app.Dependencies.Clone())
cloned.ErrorHandler = app.ErrorHandler
cloned := newApp(party, app.container.Clone())
return cloned
}

View File

@@ -12,29 +12,6 @@ func getPathParamsForInput(startParamIndex int, params []macro.TemplateParam, fu
return
}
// consumedParams := make(map[int]bool, 0)
// for _, in := range funcIn {
// for j, p := range params {
// if _, consumed := consumedParams[j]; consumed {
// continue
// }
// // fmt.Printf("%s input arg type vs %s param type\n", in.Kind().String(), p.Type.Kind().String())
// if m := macros.Lookup(p.Type); m != nil && m.GoType == in.Kind() {
// consumedParams[j] = true
// // fmt.Printf("param.go: bind path param func for paramName = '%s' and paramType = '%s'\n", paramName, paramType.String())
// funcDep, ok := context.ParamResolverByKindAndIndex(m.GoType, p.Index)
// // funcDep, ok := context.ParamResolverByKindAndKey(in.Kind(), paramName)
// if !ok {
// // here we can add a logger about invalid parameter type although it should never happen here
// // unless the end-developer modified the macro/macros with a special type but not the context/ParamResolvers.
// continue
// }
// values = append(values, funcDep)
// }
// }
// }
consumed := make(map[int]struct{})
for _, in := range funcIn {
for j, param := range params {

View File

@@ -3,12 +3,12 @@ package mvc
import (
"reflect"
"github.com/kataras/iris/v12/hero/di"
"github.com/kataras/iris/v12/hero"
)
var (
baseControllerTyp = reflect.TypeOf((*BaseController)(nil)).Elem()
errorHandlerTyp = reflect.TypeOf((*di.ErrorHandler)(nil)).Elem()
errorHandlerTyp = reflect.TypeOf((*hero.ErrorHandler)(nil)).Elem()
errorTyp = reflect.TypeOf((*error)(nil)).Elem()
)
@@ -16,6 +16,17 @@ func isBaseController(ctrlTyp reflect.Type) bool {
return ctrlTyp.Implements(baseControllerTyp)
}
// indirectType returns the value of a pointer-type "typ".
// If "typ" is a pointer, array, chan, map or slice it returns its Elem,
// otherwise returns the typ as it's.
func indirectType(typ reflect.Type) reflect.Type {
switch typ.Kind() {
case reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
return typ.Elem()
}
return typ
}
func isErrorHandler(ctrlTyp reflect.Type) bool {
return ctrlTyp.Implements(errorHandlerTyp)
}