container.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package hero
  2. import (
  3. stdContext "context"
  4. "errors"
  5. "net"
  6. "net/http"
  7. "reflect"
  8. "time"
  9. "github.com/kataras/iris/context"
  10. "github.com/kataras/iris/sessions"
  11. "github.com/kataras/golog"
  12. )
  13. // Default is the default container value which can be used for dependencies share.
  14. var Default = New()
  15. // Container contains and delivers the Dependencies that will be binded
  16. // to the controller(s) or handler(s) that can be created
  17. // using the Container's `Handler` and `Struct` methods.
  18. //
  19. // This is not exported for being used by everyone, use it only when you want
  20. // to share containers between multi mvc.go#Application
  21. // or make custom hero handlers that can be used on the standard
  22. // iris' APIBuilder.
  23. //
  24. // For a more high-level structure please take a look at the "mvc.go#Application".
  25. type Container struct {
  26. // Sorter specifies how the inputs should be sorted before binded.
  27. // Defaults to sort by "thinnest" target empty interface.
  28. Sorter Sorter
  29. // The dependencies entries.
  30. Dependencies []*Dependency
  31. // GetErrorHandler should return a valid `ErrorHandler` to handle bindings AND handler dispatch errors.
  32. // Defaults to a functon which returns the `DefaultErrorHandler`.
  33. GetErrorHandler func(*context.Context) ErrorHandler // cannot be nil.
  34. // resultHandlers is a list of functions that serve the return struct value of a function handler.
  35. // Defaults to "defaultResultHandler" but it can be overridden.
  36. resultHandlers []func(next ResultHandler) ResultHandler
  37. }
  38. // BuiltinDependencies is a list of builtin dependencies that are added on Container's initilization.
  39. // Contains the iris context, standard context, iris sessions and time dependencies.
  40. var BuiltinDependencies = []*Dependency{
  41. // iris context dependency.
  42. NewDependency(func(ctx *context.Context) *context.Context { return ctx }).Explicitly(),
  43. // standard context dependency.
  44. NewDependency(func(ctx *context.Context) stdContext.Context {
  45. return ctx.Request().Context()
  46. }).Explicitly(),
  47. // iris session dependency.
  48. NewDependency(func(ctx *context.Context) *sessions.Session {
  49. session := sessions.Get(ctx)
  50. if session == nil {
  51. panic("binding: session is nil - app.Use(sess.Handler()) to fix it")
  52. }
  53. return session
  54. }).Explicitly(),
  55. // application's logger.
  56. NewDependency(func(ctx *context.Context) *golog.Logger {
  57. return ctx.Application().Logger()
  58. }).Explicitly(),
  59. // time.Time to time.Now dependency.
  60. NewDependency(func(ctx *context.Context) time.Time {
  61. return time.Now()
  62. }).Explicitly(),
  63. // standard http Request dependency.
  64. NewDependency(func(ctx *context.Context) *http.Request {
  65. return ctx.Request()
  66. }).Explicitly(),
  67. // standard http ResponseWriter dependency.
  68. NewDependency(func(ctx *context.Context) http.ResponseWriter {
  69. return ctx.ResponseWriter()
  70. }).Explicitly(),
  71. // http headers dependency.
  72. NewDependency(func(ctx *context.Context) http.Header {
  73. return ctx.Request().Header
  74. }).Explicitly(),
  75. // Client IP.
  76. NewDependency(func(ctx *context.Context) net.IP {
  77. return net.ParseIP(ctx.RemoteAddr())
  78. }).Explicitly(),
  79. // payload and param bindings are dynamically allocated and declared at the end of the `binding` source file.
  80. }
  81. // New returns a new Container, a container for dependencies and a factory
  82. // for handlers and controllers, this is used internally by the `mvc#Application` structure.
  83. // Please take a look at the structure's documentation for more information.
  84. func New(dependencies ...interface{}) *Container {
  85. deps := make([]*Dependency, len(BuiltinDependencies))
  86. copy(deps, BuiltinDependencies)
  87. c := &Container{
  88. Sorter: sortByNumMethods,
  89. Dependencies: deps,
  90. GetErrorHandler: func(*context.Context) ErrorHandler {
  91. return DefaultErrorHandler
  92. },
  93. }
  94. for _, dependency := range dependencies {
  95. c.Register(dependency)
  96. }
  97. return c
  98. }
  99. // Clone returns a new cloned container.
  100. // It copies the ErrorHandler, Dependencies and all Options from "c" receiver.
  101. func (c *Container) Clone() *Container {
  102. cloned := New()
  103. cloned.GetErrorHandler = c.GetErrorHandler
  104. cloned.Sorter = c.Sorter
  105. clonedDeps := make([]*Dependency, len(c.Dependencies))
  106. copy(clonedDeps, c.Dependencies)
  107. cloned.Dependencies = clonedDeps
  108. cloned.resultHandlers = c.resultHandlers
  109. return cloned
  110. }
  111. // Register adds a dependency.
  112. // The value can be a single struct value-instance or a function
  113. // which has one input and one output, that output type
  114. // will be binded to the handler's input argument, if matching.
  115. //
  116. // Usage:
  117. // - Register(loggerService{prefix: "dev"})
  118. // - Register(func(ctx iris.Context) User {...})
  119. // - Register(func(User) OtherResponse {...})
  120. func Register(dependency interface{}) *Dependency {
  121. return Default.Register(dependency)
  122. }
  123. // Register adds a dependency.
  124. // The value can be a single struct value or a function.
  125. // Follow the rules:
  126. // * <T>{structValue}
  127. // * func(accepts <T>) returns <D> or (<D>, error)
  128. // * func(accepts iris.Context) returns <D> or (<D>, error)
  129. // * func(accepts1 iris.Context, accepts2 *hero.Input) returns <D> or (<D>, error)
  130. //
  131. // A Dependency can accept a previous registered dependency and return a new one or the same updated.
  132. // * func(accepts1 <D>, accepts2 <T>) returns <E> or (<E>, error) or error
  133. // * func(acceptsPathParameter1 string, id uint64) returns <T> or (<T>, error)
  134. //
  135. // Usage:
  136. //
  137. // - Register(loggerService{prefix: "dev"})
  138. // - Register(func(ctx iris.Context) User {...})
  139. // - Register(func(User) OtherResponse {...})
  140. func (c *Container) Register(dependency interface{}) *Dependency {
  141. d := NewDependency(dependency, c.Dependencies...)
  142. if d.DestType == nil {
  143. // prepend the dynamic dependency so it will be tried at the end
  144. // (we don't care about performance here, design-time)
  145. c.Dependencies = append([]*Dependency{d}, c.Dependencies...)
  146. } else {
  147. c.Dependencies = append(c.Dependencies, d)
  148. }
  149. return d
  150. }
  151. // UseResultHandler adds a result handler to the Container.
  152. // A result handler can be used to inject the returned struct value
  153. // from a request handler or to replace the default renderer.
  154. func (c *Container) UseResultHandler(handler func(next ResultHandler) ResultHandler) *Container {
  155. c.resultHandlers = append(c.resultHandlers, handler)
  156. return c
  157. }
  158. // Handler accepts a "handler" function which can accept any input arguments that match
  159. // with the Container's `Dependencies` and any output result; like string, int (string,int),
  160. // custom structs, Result(View | Response) and anything you can imagine.
  161. // It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application,
  162. // as middleware or as simple route handler or subdomain's handler.
  163. func Handler(fn interface{}) context.Handler {
  164. return Default.Handler(fn)
  165. }
  166. // Handler accepts a handler "fn" function which can accept any input arguments that match
  167. // with the Container's `Dependencies` and any output result; like string, int (string,int),
  168. // custom structs, Result(View | Response) and more.
  169. // It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application,
  170. // as middleware or as simple route handler or subdomain's handler.
  171. func (c *Container) Handler(fn interface{}) context.Handler {
  172. return c.HandlerWithParams(fn, 0)
  173. }
  174. // HandlerWithParams same as `Handler` but it can receive a total path parameters counts
  175. // to resolve coblex path parameters input dependencies.
  176. func (c *Container) HandlerWithParams(fn interface{}, paramsCount int) context.Handler {
  177. return makeHandler(fn, c, paramsCount)
  178. }
  179. // Struct accepts a pointer to a struct value and returns a structure which
  180. // contains bindings for the struct's fields and a method to
  181. // extract a Handler from this struct's method.
  182. func (c *Container) Struct(ptrValue interface{}, partyParamsCount int) *Struct {
  183. return makeStruct(ptrValue, c, partyParamsCount)
  184. }
  185. // ErrMissingDependency may returned only from the `Container.Inject` method
  186. // when not a matching dependency found for "toPtr".
  187. var ErrMissingDependency = errors.New("missing dependency")
  188. // Inject SHOULD only be used outside of HTTP handlers (performance is not priority for this method)
  189. // as it does not pre-calculate the available list of bindings for the "toPtr" and the registered dependencies.
  190. //
  191. // It sets a static-only matching dependency to the value of "toPtr".
  192. // The parameter "toPtr" SHOULD be a pointer to a value corresponding to a dependency,
  193. // like input parameter of a handler or field of a struct.
  194. //
  195. // If no matching dependency found, the `Inject` method returns an `ErrMissingDependency` and
  196. // "toPtr" keeps its original state (e.g. nil).
  197. //
  198. // Example Code:
  199. // c.Register(&LocalDatabase{...})
  200. // [...]
  201. // var db Database
  202. // err := c.Inject(&db)
  203. func (c *Container) Inject(toPtr interface{}) error {
  204. val := reflect.Indirect(valueOf(toPtr))
  205. typ := val.Type()
  206. for _, d := range c.Dependencies {
  207. if d.Static && matchDependency(d, typ) {
  208. v, err := d.Handle(nil, &Input{Type: typ})
  209. if err != nil {
  210. if err == ErrSeeOther {
  211. continue
  212. }
  213. return err
  214. }
  215. val.Set(v)
  216. return nil
  217. }
  218. }
  219. return ErrMissingDependency
  220. }