handler.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package hero
  2. import (
  3. "fmt"
  4. "reflect"
  5. "github.com/kataras/iris/v12/context"
  6. )
  7. type (
  8. // ErrorHandler describes an interface to handle errors per hero handler and its dependencies.
  9. //
  10. // Handles non-nil errors return from a hero handler or a controller's method (see `getBindingsFor` and `Handler`)
  11. // the error may return from a request-scoped dependency too (see `Handler`).
  12. ErrorHandler interface {
  13. HandleError(*context.Context, error)
  14. }
  15. // ErrorHandlerFunc implements the `ErrorHandler`.
  16. // It describes the type defnition for an error function handler.
  17. ErrorHandlerFunc func(*context.Context, error)
  18. // Code is a special type for status code.
  19. // It's used for a builtin dependency to map the status code given by a previous
  20. // method or middleware.
  21. // Use a type like that in order to not conflict with any developer-registered
  22. // dependencies.
  23. // Alternatively: ctx.GetStatusCode().
  24. Code int
  25. // Err is a special type for error stored in mvc responses or context.
  26. // It's used for a builtin dependency to map the error given by a previous
  27. // method or middleware.
  28. // Use a type like that in order to not conflict with any developer-registered
  29. // dependencies.
  30. // Alternatively: ctx.GetErr().
  31. Err error
  32. )
  33. // HandleError fires when a non-nil error returns from a request-scoped dependency at serve-time or the handler itself.
  34. func (fn ErrorHandlerFunc) HandleError(ctx *context.Context, err error) {
  35. fn(ctx, err)
  36. }
  37. // String implements the fmt.Stringer interface.
  38. // Returns the text corresponding to this status code, e.g. "Not Found".
  39. // Same as iris.StatusText(int(code)).
  40. func (code Code) String() string {
  41. return context.StatusText(int(code))
  42. }
  43. // Value returns the underline int value.
  44. // Same as int(code).
  45. func (code Code) Value() int {
  46. return int(code)
  47. }
  48. var (
  49. // ErrSeeOther may be returned from a dependency handler to skip a specific dependency
  50. // based on custom logic.
  51. ErrSeeOther = fmt.Errorf("see other")
  52. // ErrStopExecution may be returned from a dependency handler to stop
  53. // and return the execution of the function without error (it calls ctx.StopExecution() too).
  54. // It may be occurred from request-scoped dependencies as well.
  55. ErrStopExecution = fmt.Errorf("stop execution")
  56. )
  57. var (
  58. // DefaultErrStatusCode is the default error status code (400)
  59. // when the response contains a non-nil error or a request-scoped binding error occur.
  60. DefaultErrStatusCode = 400
  61. // DefaultErrorHandler is the default error handler which is fired
  62. // when a function returns a non-nil error or a request-scoped dependency failed to binded.
  63. DefaultErrorHandler = ErrorHandlerFunc(func(ctx *context.Context, err error) {
  64. if err != ErrStopExecution {
  65. if status := ctx.GetStatusCode(); status == 0 || !context.StatusCodeNotSuccessful(status) {
  66. ctx.StatusCode(DefaultErrStatusCode)
  67. }
  68. _, _ = ctx.WriteString(err.Error())
  69. }
  70. ctx.StopExecution()
  71. })
  72. )
  73. var (
  74. irisHandlerType = reflect.TypeOf((*context.Handler)(nil)).Elem()
  75. irisHandlerFuncType = reflect.TypeOf(func(*context.Context) {})
  76. )
  77. func isIrisHandlerType(typ reflect.Type) bool {
  78. return typ == irisHandlerType || typ == irisHandlerFuncType
  79. }
  80. func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler {
  81. if fn == nil {
  82. panic("makeHandler: function is nil")
  83. }
  84. // 0. A normal handler.
  85. if handler, ok := isHandler(fn); ok {
  86. return handler
  87. }
  88. // 1. A handler which returns just an error, handle it faster.
  89. if handlerWithErr, ok := isHandlerWithError(fn); ok {
  90. return func(ctx *context.Context) {
  91. if err := handlerWithErr(ctx); err != nil {
  92. c.GetErrorHandler(ctx).HandleError(ctx, err)
  93. }
  94. }
  95. }
  96. v := valueOf(fn)
  97. typ := v.Type()
  98. numIn := typ.NumIn()
  99. bindings := getBindingsForFunc(v, c.Dependencies, c.DisablePayloadAutoBinding, paramsCount)
  100. c.fillReport(context.HandlerName(fn), bindings)
  101. // Check if it's a function that accept zero or more dependencies
  102. // and returns an Iris Handler.
  103. if paramsCount <= 0 {
  104. // println(irisHandlerType.String())
  105. if typ.NumOut() == 1 && isIrisHandlerType(typ.Out(0)) {
  106. inputs := getStaticInputs(bindings, numIn)
  107. if len(inputs) != numIn {
  108. panic(fmt.Sprintf("makeHandler: func(...<T>) iris.Handler: expected %d function input parameters but fewer static dependencies matched (%d)", numIn, len(inputs)))
  109. }
  110. handler := v.Call(inputs)[0].Interface().(context.Handler)
  111. return handler
  112. }
  113. }
  114. resultHandler := defaultResultHandler
  115. for i, lidx := 0, len(c.resultHandlers)-1; i <= lidx; i++ {
  116. resultHandler = c.resultHandlers[lidx-i](resultHandler)
  117. }
  118. return func(ctx *context.Context) {
  119. inputs := make([]reflect.Value, numIn)
  120. for _, binding := range bindings {
  121. input, err := binding.Dependency.Handle(ctx, binding.Input)
  122. if err != nil {
  123. if err == ErrSeeOther {
  124. continue
  125. }
  126. // handled inside ErrorHandler.
  127. // else if err == ErrStopExecution {
  128. // ctx.StopExecution()
  129. // return // return without error.
  130. // }
  131. c.GetErrorHandler(ctx).HandleError(ctx, err)
  132. // return [13 Sep 2020, commented that in order to be able to
  133. // give end-developer the option not only to handle the error
  134. // but to skip it if necessary, example:
  135. // read form, unknown field, continue without StopWith,
  136. // the binder should bind the method's input argument and continue
  137. // without errors. See `mvc.TestErrorHandlerContinue` test.]
  138. }
  139. // If ~an error status code is set or~ execution has stopped
  140. // from within the dependency (something went wrong while validating the request),
  141. // then stop everything and let handler fire that status code.
  142. if ctx.IsStopped() /* || context.StatusCodeNotSuccessful(ctx.GetStatusCode())*/ {
  143. return
  144. }
  145. inputs[binding.Input.Index] = input
  146. }
  147. // fmt.Printf("For func: %s | valid input deps length(%d)\n", typ.String(), len(inputs))
  148. // for idx, in := range inputs {
  149. // fmt.Printf("[%d] (%s) %#+v\n", idx, in.Type().String(), in.Interface())
  150. // }
  151. outputs := v.Call(inputs)
  152. if err := dispatchFuncResult(ctx, outputs, resultHandler); err != nil {
  153. c.GetErrorHandler(ctx).HandleError(ctx, err)
  154. }
  155. }
  156. }
  157. func isHandler(fn interface{}) (context.Handler, bool) {
  158. if handler, ok := fn.(context.Handler); ok {
  159. return handler, ok
  160. }
  161. if handler, ok := fn.(func(*context.Context)); ok {
  162. return handler, ok
  163. }
  164. return nil, false
  165. }
  166. func isHandlerWithError(fn interface{}) (func(*context.Context) error, bool) {
  167. if handlerWithErr, ok := fn.(func(*context.Context) error); ok {
  168. return handlerWithErr, true
  169. }
  170. return nil, false
  171. }