mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
dcf02480b3
Former-commit-id: 9d76c2f00766afd53cf6e591c25f861f179dd817
135 lines
3.7 KiB
Go
135 lines
3.7 KiB
Go
package hero
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/kataras/iris/v12/context"
|
|
)
|
|
|
|
type (
|
|
// 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`).
|
|
ErrorHandler interface {
|
|
HandleError(context.Context, error)
|
|
}
|
|
// ErrorHandlerFunc implements the `ErrorHandler`.
|
|
// It describes the type defnition for an error function handler.
|
|
ErrorHandlerFunc func(context.Context, error)
|
|
)
|
|
|
|
// HandleError fires when a non-nil error returns from a request-scoped dependency at serve-time or the handler itself.
|
|
func (fn ErrorHandlerFunc) HandleError(ctx context.Context, err error) {
|
|
fn(ctx, err)
|
|
}
|
|
|
|
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")
|
|
)
|
|
|
|
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
|
|
|
|
// 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.
|
|
DefaultErrorHandler = ErrorHandlerFunc(func(ctx context.Context, err error) {
|
|
if err != ErrStopExecution {
|
|
if status := ctx.GetStatusCode(); status == 0 || !context.StatusCodeNotSuccessful(status) {
|
|
ctx.StatusCode(DefaultErrStatusCode)
|
|
}
|
|
|
|
_, _ = ctx.WriteString(err.Error())
|
|
}
|
|
|
|
ctx.StopExecution()
|
|
})
|
|
)
|
|
|
|
func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler {
|
|
if fn == nil {
|
|
panic("makeHandler: function is nil")
|
|
}
|
|
|
|
// 0. A normal handler.
|
|
if handler, ok := isHandler(fn); ok {
|
|
return handler
|
|
}
|
|
|
|
// 1. A handler which returns just an error, handle it faster.
|
|
if handlerWithErr, ok := isHandlerWithError(fn); ok {
|
|
return func(ctx context.Context) {
|
|
if err := handlerWithErr(ctx); err != nil {
|
|
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
v := valueOf(fn)
|
|
numIn := v.Type().NumIn()
|
|
|
|
bindings := getBindingsForFunc(v, c.Dependencies, paramsCount)
|
|
|
|
resultHandler := defaultResultHandler
|
|
for i, lidx := 0, len(c.resultHandlers)-1; i <= lidx; i++ {
|
|
resultHandler = c.resultHandlers[lidx-i](resultHandler)
|
|
}
|
|
|
|
return func(ctx context.Context) {
|
|
inputs := make([]reflect.Value, numIn)
|
|
|
|
for _, binding := range bindings {
|
|
input, err := binding.Dependency.Handle(ctx, binding.Input)
|
|
if err != nil {
|
|
if err == ErrSeeOther {
|
|
continue
|
|
}
|
|
// handled inside ErrorHandler.
|
|
// else if err == ErrStopExecution {
|
|
// ctx.StopExecution()
|
|
// return // return without error.
|
|
// }
|
|
|
|
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
|
return
|
|
}
|
|
|
|
inputs[binding.Input.Index] = input
|
|
}
|
|
|
|
outputs := v.Call(inputs)
|
|
if err := dispatchFuncResult(ctx, outputs, resultHandler); err != nil {
|
|
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func isHandler(fn interface{}) (context.Handler, bool) {
|
|
if handler, ok := fn.(context.Handler); ok {
|
|
return handler, ok
|
|
}
|
|
|
|
if handler, ok := fn.(func(context.Context)); ok {
|
|
return handler, ok
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
func isHandlerWithError(fn interface{}) (func(context.Context) error, bool) {
|
|
if handlerWithErr, ok := fn.(func(context.Context) error); ok {
|
|
return handlerWithErr, true
|
|
}
|
|
|
|
return nil, false
|
|
}
|