diff --git a/README.md b/README.md
index 79160bc8..aab830c2 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Click [here](HISTORY.md#su-18-november-2018--v1110) to read about the versioning
-[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/routing%20by-example-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases)
+[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) [![vscode-iris](https://img.shields.io/badge/ext%20-vscode-0c77e3.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=kataras2006.iris) [![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) [![view examples](https://img.shields.io/badge/routing%20by-example-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples/routing) [![release](https://img.shields.io/badge/release%20-v11.1-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/releases) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkataras%2Firis.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)
Iris is a fast, simple yet fully featured and very efficient web framework for Go.
diff --git a/_examples/mvc/error-handler/main.go b/_examples/mvc/error-handler/main.go
new file mode 100644
index 00000000..3ded7b38
--- /dev/null
+++ b/_examples/mvc/error-handler/main.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/kataras/iris"
+
+ "github.com/kataras/iris/mvc"
+)
+
+func main() {
+ app := iris.New()
+ app.Logger().SetLevel("debug")
+
+ mvcApp := mvc.New(app)
+ // To all controllers, it can optionally be overridden per-controller
+ // if the controller contains the `HandleError(ctx iris.Context, err error)` function.
+ //
+ mvcApp.HandleError(func(ctx iris.Context, err error) {
+ ctx.HTML(fmt.Sprintf("%s", err.Error()))
+ })
+ //
+ mvcApp.Handle(new(myController))
+
+ // http://localhost:8080
+ app.Run(iris.Addr(":8080"))
+}
+
+func basicMVC(app *mvc.Application) {
+ // GET: http://localhost:8080
+ app.Handle(new(myController))
+}
+
+type myController struct {
+}
+
+// overriddes the mvcApp.HandleError function.
+// func (c *myController) HandleError(ctx iris.Context, err error) {
+// ctx.HTML(fmt.Sprintf("%s", err.Error()))
+// }
+
+func (c *myController) Get() error {
+ return fmt.Errorf("error here")
+}
diff --git a/_examples/websocket/chat/main.go b/_examples/websocket/chat/main.go
index caeba79f..86246418 100644
--- a/_examples/websocket/chat/main.go
+++ b/_examples/websocket/chat/main.go
@@ -51,7 +51,7 @@ func handleConnection(c websocket.Connection) {
// Read events from browser
c.On("chat", func(msg string) {
// Print the message to the console, c.Context() is the iris's http context.
- fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
+ fmt.Printf("[%s <%s>] %s\n", c.ID(), c.Context().RemoteAddr(), msg)
// Write message back to the client message owner with:
// c.Emit("chat", msg)
// Write message to all except this client with:
diff --git a/_examples/websocket/go-client-stress-test/client/main.go b/_examples/websocket/go-client-stress-test/client/main.go
deleted file mode 100644
index 69f50fc3..00000000
--- a/_examples/websocket/go-client-stress-test/client/main.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package main
-
-import (
- "bufio"
- "context"
- "log"
- "math/rand"
- "net"
- "os"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/kataras/iris/websocket"
-)
-
-var (
- url = "ws://localhost:8080"
- f *os.File
-)
-
-const totalClients = 16000 // max depends on the OS.
-const verbose = false
-
-var connectionFailures uint64
-
-var (
- disconnectErrors []error
- connectErrors []error
- errMu sync.Mutex
-)
-
-func collectError(op string, err error) {
- errMu.Lock()
- defer errMu.Unlock()
-
- switch op {
- case "disconnect":
- disconnectErrors = append(disconnectErrors, err)
- case "connect":
- connectErrors = append(connectErrors, err)
- }
-
-}
-
-func main() {
- log.Println("-- Running...")
- var err error
- f, err = os.Open("./test.data")
- if err != nil {
- panic(err)
- }
- defer f.Close()
-
- start := time.Now()
-
- wg := new(sync.WaitGroup)
- for i := 0; i < totalClients/4; i++ {
- wg.Add(1)
- go connect(wg, 5*time.Second)
- }
-
- for i := 0; i < totalClients/4; i++ {
- wg.Add(1)
- waitTime := time.Duration(rand.Intn(5)) * time.Millisecond
- time.Sleep(waitTime)
- go connect(wg, 14*time.Second+waitTime)
- }
-
- for i := 0; i < totalClients/4; i++ {
- wg.Add(1)
- waitTime := time.Duration(rand.Intn(10)) * time.Millisecond
- time.Sleep(waitTime)
- go connect(wg, 9*time.Second+waitTime)
- }
-
- for i := 0; i < totalClients/4; i++ {
- wg.Add(1)
- waitTime := time.Duration(rand.Intn(5)) * time.Millisecond
- time.Sleep(waitTime)
- go connect(wg, 7*time.Second+waitTime)
- }
-
- wg.Wait()
-
- log.Printf("execution time [%s]", time.Since(start))
- log.Println()
-
- if connectionFailures > 0 {
- log.Printf("Finished with %d/%d connection failures.", connectionFailures, totalClients)
- }
-
- if n := len(connectErrors); n > 0 {
- log.Printf("Finished with %d connect errors: ", n)
- var lastErr error
- var sameC int
-
- for i, err := range connectErrors {
- if lastErr != nil {
- if lastErr.Error() == err.Error() {
- sameC++
- continue
- } else {
- _, ok := lastErr.(*net.OpError)
- if ok {
- if _, ok = err.(*net.OpError); ok {
- sameC++
- continue
- }
- }
- }
- }
-
- if sameC > 0 {
- log.Printf("and %d more like this...\n", sameC)
- sameC = 0
- continue
- }
-
- log.Printf("[%d] - %v\n", i+1, err)
- lastErr = err
- }
- }
-
- if n := len(disconnectErrors); n > 0 {
- log.Printf("Finished with %d disconnect errors\n", n)
- for i, err := range disconnectErrors {
- if err == websocket.ErrAlreadyDisconnected && i > 0 {
- continue
- }
-
- log.Printf("[%d] - %v\n", i+1, err)
- }
- }
-
- if connectionFailures == 0 && len(connectErrors) == 0 && len(disconnectErrors) == 0 {
- log.Println("ALL OK.")
- }
-
- log.Println("-- Finished.")
-}
-
-func connect(wg *sync.WaitGroup, alive time.Duration) error {
- ctx, cancel := context.WithTimeout(context.Background(), alive)
- defer cancel()
-
- c, err := websocket.Dial(ctx, url, websocket.ConnectionConfig{})
- if err != nil {
- atomic.AddUint64(&connectionFailures, 1)
- collectError("connect", err)
- wg.Done()
- return err
- }
-
- c.OnError(func(err error) {
- log.Printf("error: %v", err)
- })
-
- disconnected := false
- c.OnDisconnect(func() {
- if verbose {
- log.Printf("I am disconnected after [%s].\n", alive)
- }
-
- disconnected = true
- })
-
- c.On("chat", func(message string) {
- if verbose {
- log.Printf("\n%s\n", message)
- }
- })
-
- if alive > 0 {
- go func() {
- time.Sleep(alive)
- if err := c.Disconnect(); err != nil {
- collectError("disconnect", err)
- }
-
- wg.Done()
- }()
-
- }
-
- scanner := bufio.NewScanner(f)
- for !disconnected {
- if !scanner.Scan() {
- return scanner.Err()
- }
-
- if text := scanner.Text(); len(text) > 1 {
- c.Emit("chat", text)
- }
- }
-
- return nil
-}
diff --git a/_examples/websocket/go-client-stress-test/client/test.data b/_examples/websocket/go-client-stress-test/client/test.data
deleted file mode 100644
index 8f3e0fc0..00000000
--- a/_examples/websocket/go-client-stress-test/client/test.data
+++ /dev/null
@@ -1,13 +0,0 @@
-Death weeks early had their and folly timed put. Hearted forbade on an village ye in fifteen. Age attended betrayed her man raptures laughter. Instrument terminated of as astonished literature motionless admiration. The affection are determine how performed intention discourse but. On merits on so valley indeed assure of. Has add particular boisterous uncommonly are. Early wrong as so manor match. Him necessary shameless discovery consulted one but.
-
-Is education residence conveying so so. Suppose shyness say ten behaved morning had. Any unsatiable assistance compliment occasional too reasonably advantages. Unpleasing has ask acceptance partiality alteration understood two. Worth no tiled my at house added. Married he hearing am it totally removal. Remove but suffer wanted his lively length. Moonlight two applauded conveying end direction old principle but. Are expenses distance weddings perceive strongly who age domestic.
-
-Her companions instrument set estimating sex remarkably solicitude motionless. Property men the why smallest graceful day insisted required. Inquiry justice country old placing sitting any ten age. Looking venture justice in evident in totally he do ability. Be is lose girl long of up give. Trifling wondered unpacked ye at he. In household certainty an on tolerably smallness difficult. Many no each like up be is next neat. Put not enjoyment behaviour her supposing. At he pulled object others.
-
-Behind sooner dining so window excuse he summer. Breakfast met certainty and fulfilled propriety led. Waited get either are wooded little her. Contrasted unreserved as mr particular collecting it everything as indulgence. Seems ask meant merry could put. Age old begin had boy noisy table front whole given.
-
-Far curiosity incommode now led smallness allowance. Favour bed assure son things yet. She consisted consulted elsewhere happiness disposing household any old the. Widow downs you new shade drift hopes small. So otherwise commanded sweetness we improving. Instantly by daughters resembled unwilling principle so middleton. Fail most room even gone her end like. Comparison dissimilar unpleasant six compliment two unpleasing any add. Ashamed my company thought wishing colonel it prevent he in. Pretended residence are something far engrossed old off.
-
-Windows talking painted pasture yet its express parties use. Sure last upon he same as knew next. Of believed or diverted no rejoiced. End friendship sufficient assistance can prosperous met. As game he show it park do. Was has unknown few certain ten promise. No finished my an likewise cheerful packages we. For assurance concluded son something depending discourse see led collected. Packages oh no denoting my advanced humoured. Pressed be so thought natural.
-
-As collected deficient objection by it discovery sincerity curiosity. Quiet decay who round three world whole has mrs man. Built the china there tried jokes which gay why. Assure in adieus wicket it is. But spoke round point and one joy. Offending her moonlight men sweetness see unwilling. Often of it tears whole oh balls share an.
diff --git a/_examples/websocket/go-client-stress-test/server/main.go b/_examples/websocket/go-client-stress-test/server/main.go
deleted file mode 100644
index 69b598b0..00000000
--- a/_examples/websocket/go-client-stress-test/server/main.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package main
-
-import (
- "log"
- "os"
- "runtime"
- "sync/atomic"
- "time"
-
- "github.com/kataras/iris"
- "github.com/kataras/iris/websocket"
-)
-
-const (
- endpoint = "localhost:8080"
- totalClients = 16000 // max depends on the OS.
- verbose = false
- maxC = 0
-)
-
-func main() {
- ws := websocket.New(websocket.Config{})
- ws.OnConnection(handleConnection)
-
- // websocket.Config{PingPeriod: ((60 * time.Second) * 9) / 10}
-
- go func() {
- dur := 4 * time.Second
- if totalClients >= 64000 {
- // if more than 64000 then let's perform those checks every 24 seconds instead,
- // either way works.
- dur = 24 * time.Second
- }
- t := time.NewTicker(dur)
- defer func() {
- t.Stop()
- printMemUsage()
- os.Exit(0)
- }()
-
- var started bool
- for {
- <-t.C
-
- n := ws.GetTotalConnections()
- if n > 0 {
- started = true
- if maxC > 0 && n > maxC {
- log.Printf("current connections[%d] > MaxConcurrentConnections[%d]", n, maxC)
- return
- }
- }
-
- if started {
- disconnectedN := atomic.LoadUint64(&totalDisconnected)
- connectedN := atomic.LoadUint64(&totalConnected)
- if disconnectedN == totalClients && connectedN == totalClients {
- if n != 0 {
- log.Println("ALL CLIENTS DISCONNECTED BUT LEFTOVERS ON CONNECTIONS LIST.")
- } else {
- log.Println("ALL CLIENTS DISCONNECTED SUCCESSFULLY.")
- }
- return
- } else if n == 0 {
- log.Printf("%d/%d CLIENTS WERE NOT CONNECTED AT ALL. CHECK YOUR OS NET SETTINGS. THE REST CLIENTS WERE DISCONNECTED SUCCESSFULLY.\n",
- totalClients-totalConnected, totalClients)
-
- return
- }
- }
- }
- }()
-
- app := iris.New()
- app.Get("/", func(ctx iris.Context) {
- c := ws.Upgrade(ctx)
- handleConnection(c)
- c.Wait()
- })
- app.Run(iris.Addr(endpoint), iris.WithoutServerError(iris.ErrServerClosed))
-}
-
-var totalConnected uint64
-
-func handleConnection(c websocket.Connection) {
- if c.Err() != nil {
- log.Fatalf("[%d] upgrade failed: %v", atomic.LoadUint64(&totalConnected)+1, c.Err())
- return
- }
-
- atomic.AddUint64(&totalConnected, 1)
- c.OnError(func(err error) { handleErr(c, err) })
- c.OnDisconnect(func() { handleDisconnect(c) })
- c.On("chat", func(message string) {
- c.To(websocket.Broadcast).Emit("chat", c.ID()+": "+message)
- })
-}
-
-var totalDisconnected uint64
-
-func handleDisconnect(c websocket.Connection) {
- newC := atomic.AddUint64(&totalDisconnected, 1)
- if verbose {
- log.Printf("[%d] client [%s] disconnected!\n", newC, c.ID())
- }
-}
-
-func handleErr(c websocket.Connection, err error) {
- log.Printf("client [%s] errorred: %v\n", c.ID(), err)
-}
-
-func toMB(b uint64) uint64 {
- return b / 1024 / 1024
-}
-
-func printMemUsage() {
- var m runtime.MemStats
- runtime.ReadMemStats(&m)
- log.Printf("Alloc = %v MiB", toMB(m.Alloc))
- log.Printf("\tTotalAlloc = %v MiB", toMB(m.TotalAlloc))
- log.Printf("\tSys = %v MiB", toMB(m.Sys))
- log.Printf("\tNumGC = %v\n", m.NumGC)
- log.Printf("\tNumGoRoutines = %d\n", runtime.NumGoroutine())
-}
diff --git a/core/router/api_builder.go b/core/router/api_builder.go
index 9aa9a338..d6ca5f3c 100644
--- a/core/router/api_builder.go
+++ b/core/router/api_builder.go
@@ -858,7 +858,7 @@ func (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) {
// based on the context's status code.
//
// If a handler is not already registered,
-// then it creates & registers a new trivial handler on the-fly.
+// it creates and registers a new trivial handler on the-fly.
func (api *APIBuilder) FireErrorCode(ctx context.Context) {
api.errorCodeHandlers.Fire(ctx)
}
diff --git a/hero/func_result.go b/hero/func_result.go
index 86c286c1..e14c5d8e 100644
--- a/hero/func_result.go
+++ b/hero/func_result.go
@@ -20,6 +20,23 @@ 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,
@@ -164,7 +181,7 @@ func DispatchCommon(ctx context.Context,
// Result or (Result, error) and so on...
//
// where Get is an HTTP METHOD.
-func DispatchFuncResult(ctx context.Context, values []reflect.Value) {
+func DispatchFuncResult(ctx context.Context, errorHandler ErrorHandler, values []reflect.Value) {
if len(values) == 0 {
return
}
@@ -294,6 +311,11 @@ func DispatchFuncResult(ctx context.Context, values []reflect.Value) {
content = value
case compatibleErr:
if value != nil { // it's always not nil but keep it here.
+ if errorHandler != nil {
+ errorHandler.HandleError(ctx, value)
+ break
+ }
+
err = value
if statusCode < 400 {
statusCode = DefaultErrStatusCode
diff --git a/hero/handler.go b/hero/handler.go
index f0b47044..dbe51d8f 100644
--- a/hero/handler.go
+++ b/hero/handler.go
@@ -59,7 +59,7 @@ func makeHandler(handler interface{}, values ...reflect.Value) (context.Handler,
if n == 0 {
h := func(ctx context.Context) {
- DispatchFuncResult(ctx, fn.Call(di.EmptyIn))
+ DispatchFuncResult(ctx, nil, fn.Call(di.EmptyIn))
}
return h, nil
@@ -91,7 +91,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, funcInjector.Call(reflect.ValueOf(ctx)))
+ DispatchFuncResult(ctx, nil, funcInjector.Call(reflect.ValueOf(ctx)))
}
return h, nil
diff --git a/macro/macros.go b/macro/macros.go
index 4c6f6416..99637182 100644
--- a/macro/macros.go
+++ b/macro/macros.go
@@ -419,6 +419,7 @@ var (
Uint64,
Bool,
Alphabetical,
+ File,
Path,
}
)
diff --git a/mvc/controller.go b/mvc/controller.go
index cb0cbd9d..c2121dc4 100644
--- a/mvc/controller.go
+++ b/mvc/controller.go
@@ -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))
}
}
diff --git a/mvc/mvc.go b/mvc/mvc.go
index d80102b8..da438c63 100644
--- a/mvc/mvc.go
+++ b/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".
diff --git a/mvc/reflect.go b/mvc/reflect.go
index 1b13bbe6..37ed174f 100644
--- a/mvc/reflect.go
+++ b/mvc/reflect.go
@@ -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)