mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
implement mvc HandleError as requested at #1244
Former-commit-id: 58a69f9cffe67c3aa1bab5d9425c5df65e2367ed
This commit is contained in:
parent
df3a68255c
commit
0d4d2bd3fa
|
@ -6,7 +6,7 @@ Click [here](HISTORY.md#su-18-november-2018--v1110) to read about the versioning
|
|||
|
||||
<a href="https://iris-go.com"> <img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" /> </a>
|
||||
|
||||
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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)<!-- [![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)--> [![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)<!--[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)--> [![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.
|
||||
|
||||
|
|
44
_examples/mvc/error-handler/main.go
Normal file
44
_examples/mvc/error-handler/main.go
Normal file
|
@ -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("<b>%s</b>", 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("<i>%s</i>", err.Error()))
|
||||
// }
|
||||
|
||||
func (c *myController) Get() error {
|
||||
return fmt.Errorf("error here")
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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.
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -419,6 +419,7 @@ var (
|
|||
Uint64,
|
||||
Bool,
|
||||
Alphabetical,
|
||||
File,
|
||||
Path,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user