mirror of
https://github.com/kataras/iris.git
synced 2025-12-24 05:17:03 +00:00
implement mvc HandleError as requested at #1244
Former-commit-id: 58a69f9cffe67c3aa1bab5d9425c5df65e2367ed
This commit is contained in:
@@ -29,7 +29,7 @@ type shared interface {
|
||||
Handle(httpMethod, path, funcName string, middleware ...context.Handler) *router.Route
|
||||
}
|
||||
|
||||
// BeforeActivation is being used as the onle one input argument of a
|
||||
// BeforeActivation is being used as the only one input argument of a
|
||||
// `func(c *Controller) BeforeActivation(b mvc.BeforeActivation) {}`.
|
||||
//
|
||||
// It's being called before the controller's dependencies binding to the fields or the input arguments
|
||||
@@ -42,11 +42,11 @@ type BeforeActivation interface {
|
||||
Dependencies() *di.Values
|
||||
}
|
||||
|
||||
// AfterActivation is being used as the onle one input argument of a
|
||||
// AfterActivation is being used as the only one input argument of a
|
||||
// `func(c *Controller) AfterActivation(a mvc.AfterActivation) {}`.
|
||||
//
|
||||
// It's being called after the `BeforeActivation`,
|
||||
// and after controller's dependencies binded to the fields or the input arguments but before server ran.
|
||||
// and after controller's dependencies bind-ed to the fields or the input arguments but before server ran.
|
||||
//
|
||||
// It's being used to customize a controller if needed inside the controller itself,
|
||||
// it's called once per application.
|
||||
@@ -81,10 +81,12 @@ type ControllerActivator struct {
|
||||
routes map[string]*router.Route
|
||||
|
||||
// the bindings that comes from the Engine and the controller's filled fields if any.
|
||||
// Can be binded to the the new controller's fields and method that is fired
|
||||
// Can be bind-ed to the the new controller's fields and method that is fired
|
||||
// on incoming requests.
|
||||
dependencies di.Values
|
||||
|
||||
errorHandler hero.ErrorHandler
|
||||
|
||||
// initialized on the first `Handle`.
|
||||
injector *di.StructInjector
|
||||
}
|
||||
@@ -101,7 +103,7 @@ func NameOf(v interface{}) string {
|
||||
return fullname
|
||||
}
|
||||
|
||||
func newControllerActivator(router router.Party, controller interface{}, dependencies []reflect.Value) *ControllerActivator {
|
||||
func newControllerActivator(router router.Party, controller interface{}, dependencies []reflect.Value, errorHandler hero.ErrorHandler) *ControllerActivator {
|
||||
typ := reflect.TypeOf(controller)
|
||||
|
||||
c := &ControllerActivator{
|
||||
@@ -121,6 +123,7 @@ func newControllerActivator(router router.Party, controller interface{}, depende
|
||||
routes: whatReservedMethods(typ),
|
||||
// CloneWithFieldsOf: include the manual fill-ed controller struct's fields to the dependencies.
|
||||
dependencies: di.Values(dependencies).CloneWithFieldsOf(controller),
|
||||
errorHandler: errorHandler,
|
||||
}
|
||||
|
||||
return c
|
||||
@@ -326,7 +329,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
||||
// Remember:
|
||||
// The `Handle->handlerOf` can be called from `BeforeActivation` event
|
||||
// then, the c.injector is nil because
|
||||
// we may not have the dependencies binded yet.
|
||||
// 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.
|
||||
@@ -346,24 +349,27 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
||||
}
|
||||
|
||||
var (
|
||||
implementsBase = isBaseController(c.Type)
|
||||
hasBindableFields = c.injector.CanInject
|
||||
hasBindableFuncInputs = funcInjector.Has
|
||||
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 {
|
||||
if !implementsBase && !hasBindableFields && !hasBindableFuncInputs && !implementsErrorHandler {
|
||||
return func(ctx context.Context) {
|
||||
hero.DispatchFuncResult(ctx, call(c.injector.AcquireSlice()))
|
||||
hero.DispatchFuncResult(ctx, c.errorHandler, call(c.injector.AcquireSlice()))
|
||||
}
|
||||
}
|
||||
|
||||
n := m.Type.NumIn()
|
||||
return func(ctx context.Context) {
|
||||
var (
|
||||
ctrl = c.injector.Acquire()
|
||||
ctxValue reflect.Value
|
||||
ctrl = c.injector.Acquire()
|
||||
ctxValue reflect.Value
|
||||
errorHandler = c.errorHandler
|
||||
)
|
||||
|
||||
// inject struct fields first before the BeginRequest and EndRequest, if any,
|
||||
@@ -388,6 +394,10 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
||||
defer b.EndRequest(ctx)
|
||||
}
|
||||
|
||||
if funcHasErrorOut && implementsErrorHandler {
|
||||
errorHandler = ctrl.Interface().(hero.ErrorHandler)
|
||||
}
|
||||
|
||||
if hasBindableFuncInputs {
|
||||
// means that ctxValue is not initialized before by the controller's struct injector.
|
||||
if !hasBindableFields {
|
||||
@@ -406,11 +416,11 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
||||
// println("controller.go: execution: in.Value = "+inn.String()+" and in.Type = "+inn.Type().Kind().String()+" of index: ", idxx)
|
||||
// }
|
||||
|
||||
hero.DispatchFuncResult(ctx, call(in))
|
||||
hero.DispatchFuncResult(ctx, errorHandler, call(in))
|
||||
return
|
||||
}
|
||||
|
||||
hero.DispatchFuncResult(ctx, ctrl.Method(m.Index).Call(emptyIn))
|
||||
hero.DispatchFuncResult(ctx, errorHandler, ctrl.Method(m.Index).Call(emptyIn))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
21
mvc/mvc.go
21
mvc/mvc.go
@@ -27,7 +27,7 @@ var (
|
||||
HeroDependencies = true
|
||||
)
|
||||
|
||||
// Application is the high-level compoment of the "mvc" package.
|
||||
// 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.
|
||||
// It contains the Router(iris.Party) in order to be able to register
|
||||
@@ -42,6 +42,7 @@ type Application struct {
|
||||
Dependencies di.Values
|
||||
Router router.Party
|
||||
Controllers []*ControllerActivator
|
||||
ErrorHandler hero.ErrorHandler
|
||||
}
|
||||
|
||||
func newApp(subRouter router.Party, values di.Values) *Application {
|
||||
@@ -99,7 +100,7 @@ func (app *Application) Configure(configurators ...func(*Application)) *Applicat
|
||||
// The value can be a single struct value-instance or a function
|
||||
// which has one input and one output, the input should be
|
||||
// an `iris.Context` and the output can be any type, that output type
|
||||
// will be binded to the controller's field, if matching or to the
|
||||
// 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,
|
||||
@@ -172,7 +173,7 @@ Set the Logger's Level to "debug" to view the active dependencies per controller
|
||||
// Examples at: https://github.com/kataras/iris/tree/master/_examples/mvc
|
||||
func (app *Application) Handle(controller interface{}) *Application {
|
||||
// initialize the controller's activator, nothing too magical so far.
|
||||
c := newControllerActivator(app.Router, controller, app.Dependencies)
|
||||
c := newControllerActivator(app.Router, controller, app.Dependencies, app.ErrorHandler)
|
||||
|
||||
// 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
|
||||
@@ -195,12 +196,22 @@ func (app *Application) Handle(controller interface{}) *Application {
|
||||
return app
|
||||
}
|
||||
|
||||
// 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(errHandler hero.ErrorHandlerFunc) *Application {
|
||||
app.ErrorHandler = errHandler
|
||||
return app
|
||||
}
|
||||
|
||||
// Clone returns a new mvc Application which has the dependencies
|
||||
// of the current mvc Mpplication's dependencies.
|
||||
// of the current mvc Application's `Dependencies` and its `ErrorHandler`.
|
||||
//
|
||||
// Example: `.Clone(app.Party("/path")).Handle(new(TodoSubController))`.
|
||||
func (app *Application) Clone(party router.Party) *Application {
|
||||
return newApp(party, app.Dependencies.Clone())
|
||||
cloned := newApp(party, app.Dependencies.Clone())
|
||||
cloned.ErrorHandler = app.ErrorHandler
|
||||
return cloned
|
||||
}
|
||||
|
||||
// Party returns a new child mvc Application based on the current path + "relativePath".
|
||||
|
||||
@@ -1,13 +1,42 @@
|
||||
package mvc
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
var baseControllerTyp = reflect.TypeOf((*BaseController)(nil)).Elem()
|
||||
"github.com/kataras/iris/hero"
|
||||
)
|
||||
|
||||
var (
|
||||
baseControllerTyp = reflect.TypeOf((*BaseController)(nil)).Elem()
|
||||
errorHandlerTyp = reflect.TypeOf((*hero.ErrorHandler)(nil)).Elem()
|
||||
errorTyp = reflect.TypeOf((*error)(nil)).Elem()
|
||||
)
|
||||
|
||||
func isBaseController(ctrlTyp reflect.Type) bool {
|
||||
return ctrlTyp.Implements(baseControllerTyp)
|
||||
}
|
||||
|
||||
func isErrorHandler(ctrlTyp reflect.Type) bool {
|
||||
return ctrlTyp.Implements(errorHandlerTyp)
|
||||
}
|
||||
|
||||
func hasErrorOutArgs(fn reflect.Method) bool {
|
||||
n := fn.Type.NumOut()
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
if out := fn.Type.Out(i); out.Kind() == reflect.Interface {
|
||||
if out.Implements(errorTyp) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getInputArgsFromFunc(funcTyp reflect.Type) []reflect.Type {
|
||||
n := funcTyp.NumIn()
|
||||
funcIn := make([]reflect.Type, n, n)
|
||||
|
||||
Reference in New Issue
Block a user