handler.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package hero
  2. import (
  3. "fmt"
  4. "reflect"
  5. "runtime"
  6. "github.com/kataras/iris/hero/di"
  7. "github.com/kataras/golog"
  8. "github.com/kataras/iris/context"
  9. )
  10. var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem()
  11. // IsContext returns true if the "inTyp" is a type of Context.
  12. func IsContext(inTyp reflect.Type) bool {
  13. return inTyp.Implements(contextTyp)
  14. }
  15. // checks if "handler" is context.Handler: func(context.Context).
  16. func isContextHandler(handler interface{}) (context.Handler, bool) {
  17. h, is := handler.(context.Handler)
  18. if !is {
  19. fh, is := handler.(func(context.Context))
  20. if is {
  21. return fh, is
  22. }
  23. }
  24. return h, is
  25. }
  26. func validateHandler(handler interface{}) error {
  27. if typ := reflect.TypeOf(handler); !di.IsFunc(typ) {
  28. return fmt.Errorf("handler expected to be a kind of func but got typeof(%s)", typ.String())
  29. }
  30. return nil
  31. }
  32. // makeHandler accepts a "handler" function which can accept any input arguments that match
  33. // with the "values" types and any output result, that matches the hero types, like string, int (string,int),
  34. // custom structs, Result(View | Response) and anything that you can imagine,
  35. // and returns a low-level `context/iris.Handler` which can be used anywhere in the Iris Application,
  36. // as middleware or as simple route handler or party handler or subdomain handler-router.
  37. func makeHandler(handler interface{}, values ...reflect.Value) (context.Handler, error) {
  38. if err := validateHandler(handler); err != nil {
  39. return nil, err
  40. }
  41. if h, is := isContextHandler(handler); is {
  42. golog.Warnf("the standard API to register a context handler could be used instead")
  43. return h, nil
  44. }
  45. fn := reflect.ValueOf(handler)
  46. n := fn.Type().NumIn()
  47. if n == 0 {
  48. h := func(ctx context.Context) {
  49. DispatchFuncResult(ctx, fn.Call(di.EmptyIn))
  50. }
  51. return h, nil
  52. }
  53. funcInjector := di.Func(fn, values...)
  54. valid := funcInjector.Length == n
  55. if !valid {
  56. // is invalid when input len and values are not match
  57. // or their types are not match, we will take look at the
  58. // second statement, here we will re-try it
  59. // using binders for path parameters: string, int, int64, bool.
  60. // We don't have access to the path, so neither to the macros here,
  61. // but in mvc. So we have to do it here.
  62. if valid = funcInjector.Retry(new(params).resolve); !valid {
  63. pc := fn.Pointer()
  64. fpc := runtime.FuncForPC(pc)
  65. callerFileName, callerLineNumber := fpc.FileLine(pc)
  66. callerName := fpc.Name()
  67. err := fmt.Errorf("input arguments length(%d) and valid binders length(%d) are not equal for typeof '%s' which is defined at %s:%d by %s",
  68. n, funcInjector.Length, fn.Type().String(), callerFileName, callerLineNumber, callerName)
  69. return nil, err
  70. }
  71. }
  72. h := func(ctx context.Context) {
  73. // in := make([]reflect.Value, n, n)
  74. // funcInjector.Inject(&in, reflect.ValueOf(ctx))
  75. // DispatchFuncResult(ctx, fn.Call(in))
  76. DispatchFuncResult(ctx, funcInjector.Call(reflect.ValueOf(ctx)))
  77. }
  78. return h, nil
  79. }