2017-12-25 19:05:32 +01:00
|
|
|
package hero
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
|
2019-10-25 00:27:02 +02:00
|
|
|
"github.com/kataras/iris/v12/context"
|
2020-02-29 13:18:15 +01:00
|
|
|
)
|
2017-12-25 19:05:32 +01:00
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
type (
|
2020-03-01 07:34:53 +01:00
|
|
|
// ErrorHandler describes an interface to handle errors per hero handler and its dependencies.
|
|
|
|
//
|
|
|
|
// Handles non-nil errors return from a hero handler or a controller's method (see `getBindingsFor` and `Handler`)
|
|
|
|
// the error may return from a request-scoped dependency too (see `Handler`).
|
2020-02-29 13:18:15 +01:00
|
|
|
ErrorHandler interface {
|
2020-07-10 22:21:09 +02:00
|
|
|
HandleError(*context.Context, error)
|
2020-02-29 13:18:15 +01:00
|
|
|
}
|
2020-03-01 07:34:53 +01:00
|
|
|
// ErrorHandlerFunc implements the `ErrorHandler`.
|
|
|
|
// It describes the type defnition for an error function handler.
|
2020-07-10 22:21:09 +02:00
|
|
|
ErrorHandlerFunc func(*context.Context, error)
|
2020-08-22 07:04:22 +02:00
|
|
|
|
|
|
|
// Code is a special type for status code.
|
|
|
|
Code int
|
2017-12-25 19:05:32 +01:00
|
|
|
)
|
|
|
|
|
2020-03-01 07:34:53 +01:00
|
|
|
// HandleError fires when a non-nil error returns from a request-scoped dependency at serve-time or the handler itself.
|
2020-07-10 22:21:09 +02:00
|
|
|
func (fn ErrorHandlerFunc) HandleError(ctx *context.Context, err error) {
|
2020-02-29 13:18:15 +01:00
|
|
|
fn(ctx, err)
|
|
|
|
}
|
2017-12-25 19:05:32 +01:00
|
|
|
|
2020-03-01 01:17:19 +01:00
|
|
|
var (
|
|
|
|
// ErrSeeOther may be returned from a dependency handler to skip a specific dependency
|
|
|
|
// based on custom logic.
|
|
|
|
ErrSeeOther = fmt.Errorf("see other")
|
|
|
|
// ErrStopExecution may be returned from a dependency handler to stop
|
|
|
|
// and return the execution of the function without error (it calls ctx.StopExecution() too).
|
|
|
|
// It may be occurred from request-scoped dependencies as well.
|
|
|
|
ErrStopExecution = fmt.Errorf("stop execution")
|
|
|
|
)
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
var (
|
|
|
|
// DefaultErrStatusCode is the default error status code (400)
|
|
|
|
// when the response contains a non-nil error or a request-scoped binding error occur.
|
|
|
|
DefaultErrStatusCode = 400
|
2020-02-14 22:34:56 +01:00
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
// DefaultErrorHandler is the default error handler which is fired
|
|
|
|
// when a function returns a non-nil error or a request-scoped dependency failed to binded.
|
2020-07-10 22:21:09 +02:00
|
|
|
DefaultErrorHandler = ErrorHandlerFunc(func(ctx *context.Context, err error) {
|
2020-03-01 01:17:19 +01:00
|
|
|
if err != ErrStopExecution {
|
|
|
|
if status := ctx.GetStatusCode(); status == 0 || !context.StatusCodeNotSuccessful(status) {
|
|
|
|
ctx.StatusCode(DefaultErrStatusCode)
|
|
|
|
}
|
|
|
|
|
2020-03-01 07:34:53 +01:00
|
|
|
_, _ = ctx.WriteString(err.Error())
|
2020-02-29 13:18:15 +01:00
|
|
|
}
|
2017-12-25 19:05:32 +01:00
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
ctx.StopExecution()
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2020-03-02 18:48:53 +01:00
|
|
|
func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler {
|
2020-02-29 13:18:15 +01:00
|
|
|
if fn == nil {
|
|
|
|
panic("makeHandler: function is nil")
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
// 0. A normal handler.
|
|
|
|
if handler, ok := isHandler(fn); ok {
|
|
|
|
return handler
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
// 1. A handler which returns just an error, handle it faster.
|
|
|
|
if handlerWithErr, ok := isHandlerWithError(fn); ok {
|
2020-07-10 22:21:09 +02:00
|
|
|
return func(ctx *context.Context) {
|
2020-02-29 13:18:15 +01:00
|
|
|
if err := handlerWithErr(ctx); err != nil {
|
|
|
|
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
|
|
|
}
|
|
|
|
}
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
v := valueOf(fn)
|
2020-05-19 08:28:27 +02:00
|
|
|
typ := v.Type()
|
|
|
|
numIn := typ.NumIn()
|
2017-12-25 19:05:32 +01:00
|
|
|
|
2020-03-02 18:48:53 +01:00
|
|
|
bindings := getBindingsForFunc(v, c.Dependencies, paramsCount)
|
2020-08-24 20:44:29 +02:00
|
|
|
c.fillReport(context.HandlerName(fn), bindings)
|
2020-02-29 13:18:15 +01:00
|
|
|
|
2020-04-18 21:40:47 +02:00
|
|
|
resultHandler := defaultResultHandler
|
|
|
|
for i, lidx := 0, len(c.resultHandlers)-1; i <= lidx; i++ {
|
|
|
|
resultHandler = c.resultHandlers[lidx-i](resultHandler)
|
|
|
|
}
|
|
|
|
|
2020-07-10 22:21:09 +02:00
|
|
|
return func(ctx *context.Context) {
|
2020-02-29 13:18:15 +01:00
|
|
|
inputs := make([]reflect.Value, numIn)
|
|
|
|
|
|
|
|
for _, binding := range bindings {
|
|
|
|
input, err := binding.Dependency.Handle(ctx, binding.Input)
|
|
|
|
if err != nil {
|
|
|
|
if err == ErrSeeOther {
|
|
|
|
continue
|
|
|
|
}
|
2020-03-01 01:17:19 +01:00
|
|
|
// handled inside ErrorHandler.
|
|
|
|
// else if err == ErrStopExecution {
|
|
|
|
// ctx.StopExecution()
|
|
|
|
// return // return without error.
|
|
|
|
// }
|
2020-02-29 13:18:15 +01:00
|
|
|
|
|
|
|
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-05 15:03:19 +02:00
|
|
|
// If ~an error status code is set or~ execution has stopped
|
|
|
|
// from within the dependency (something went wrong while validating the request),
|
|
|
|
// then stop everything and let handler fire that status code.
|
|
|
|
if ctx.IsStopped() /* || context.StatusCodeNotSuccessful(ctx.GetStatusCode())*/ {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
inputs[binding.Input.Index] = input
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|
|
|
|
|
2020-06-18 22:53:53 +02:00
|
|
|
// fmt.Printf("For func: %s | valid input deps length(%d)\n", typ.String(), len(inputs))
|
|
|
|
// for idx, in := range inputs {
|
|
|
|
// fmt.Printf("[%d] (%s) %#+v\n", idx, in.Type().String(), in.Interface())
|
|
|
|
// }
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
outputs := v.Call(inputs)
|
2020-04-18 21:40:47 +02:00
|
|
|
if err := dispatchFuncResult(ctx, outputs, resultHandler); err != nil {
|
2020-02-29 13:18:15 +01:00
|
|
|
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
|
|
|
}
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|
2020-02-29 13:18:15 +01:00
|
|
|
}
|
2017-12-25 19:05:32 +01:00
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
func isHandler(fn interface{}) (context.Handler, bool) {
|
|
|
|
if handler, ok := fn.(context.Handler); ok {
|
|
|
|
return handler, ok
|
|
|
|
}
|
|
|
|
|
2020-07-10 22:21:09 +02:00
|
|
|
if handler, ok := fn.(func(*context.Context)); ok {
|
2020-02-29 13:18:15 +01:00
|
|
|
return handler, ok
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2020-07-10 22:21:09 +02:00
|
|
|
func isHandlerWithError(fn interface{}) (func(*context.Context) error, bool) {
|
|
|
|
if handlerWithErr, ok := fn.(func(*context.Context) error); ok {
|
2020-02-29 13:18:15 +01:00
|
|
|
return handlerWithErr, true
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 13:18:15 +01:00
|
|
|
return nil, false
|
2017-12-25 19:05:32 +01:00
|
|
|
}
|