handler.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Package handler is the highest level module of the macro package which makes use the rest of the macro package,
  2. // it is mainly used, internally, by the router package.
  3. package handler
  4. import (
  5. "github.com/kataras/iris/context"
  6. "github.com/kataras/iris/core/memstore"
  7. "github.com/kataras/iris/macro"
  8. )
  9. // CanMakeHandler reports whether a macro template needs a special macro's evaluator handler to be validated
  10. // before procceed to the next handler(s).
  11. // If the template does not contain any dynamic attributes and a special handler is NOT required
  12. // then it returns false.
  13. func CanMakeHandler(tmpl macro.Template) (needsMacroHandler bool) {
  14. if len(tmpl.Params) == 0 {
  15. return
  16. }
  17. // check if we have params like: {name:string} or {name} or {anything:path} without else keyword or any functions used inside these params.
  18. // 1. if we don't have, then we don't need to add a handler before the main route's handler (as I said, no performance if macro is not really used)
  19. // 2. if we don't have any named params then we don't need a handler too.
  20. for _, p := range tmpl.Params {
  21. if p.CanEval() {
  22. // if at least one needs it, then create the handler.
  23. needsMacroHandler = true
  24. break
  25. }
  26. }
  27. return
  28. }
  29. // MakeHandler creates and returns a handler from a macro template, the handler evaluates each of the parameters if necessary at all.
  30. // If the template does not contain any dynamic attributes and a special handler is NOT required
  31. // then it returns a nil handler.
  32. func MakeHandler(tmpl macro.Template) context.Handler {
  33. filter := MakeFilter(tmpl)
  34. return func(ctx *context.Context) {
  35. if !filter(ctx) {
  36. if ctx.GetCurrentRoute().StatusErrorCode() == ctx.GetStatusCode() {
  37. ctx.Next()
  38. } else {
  39. ctx.StopExecution()
  40. }
  41. return
  42. }
  43. // if all passed or the next is the registered error handler to handle this status code,
  44. // just continue.
  45. ctx.Next()
  46. }
  47. }
  48. // MakeFilter returns a Filter which reports whether a specific macro template
  49. // and its parameters pass the serve-time validation.
  50. func MakeFilter(tmpl macro.Template) context.Filter {
  51. if !CanMakeHandler(tmpl) {
  52. return nil
  53. }
  54. return func(ctx *context.Context) bool {
  55. for _, p := range tmpl.Params {
  56. if !p.CanEval() {
  57. continue // allow.
  58. }
  59. // 07-29-2019
  60. // changed to retrieve by param index in order to support
  61. // different parameter names for routes with
  62. // different param types (and probably different param names i.e {name:string}, {id:uint64})
  63. // in the exact same path pattern.
  64. //
  65. // Same parameter names are not allowed, different param types in the same path
  66. // should have different name e.g. {name} {id:uint64};
  67. // something like {name} and {name:uint64}
  68. // is bad API design and we do NOT allow it by-design.
  69. entry, found := ctx.Params().Store.GetEntryAt(p.Index)
  70. if !found {
  71. // should never happen.
  72. return false
  73. }
  74. value := p.Eval(entry.String())
  75. if value == nil {
  76. ctx.StatusCode(p.ErrCode)
  77. return false
  78. }
  79. // Fixes binding different path parameters names,
  80. //
  81. // app.Get("/{fullname:string}", strHandler)
  82. // app.Get("/{id:int}", idHandler)
  83. //
  84. // before that user didn't see anything
  85. // but under the hoods the set-ed value was a type of string instead of type of int,
  86. // because store contained both "fullname" (which set-ed by the router itself on its string representation)
  87. // and "id" by the param evaluator (see core/router/handler.go and bindMultiParamTypesHandler->MakeFilter)
  88. // and the MVC get by index (e.g. 0) therefore
  89. // it got the "fullname" of type string instead of "id" int if /{int} requested.
  90. // which is critical for faster type assertion in the upcoming, new iris dependency injection (20 Feb 2020).
  91. ctx.Params().Store[p.Index] = memstore.Entry{
  92. Key: p.Name,
  93. ValueRaw: value,
  94. }
  95. // for i, v := range ctx.Params().Store {
  96. // fmt.Printf("[%d:%s] macro/handler/handler.go: param passed: %s(%v of type: %T)\n", i, v.Key,
  97. // p.Src, v.ValueRaw, v.ValueRaw)
  98. // }
  99. }
  100. return true
  101. }
  102. }