container.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. package hero
  2. import (
  3. stdContext "context"
  4. "errors"
  5. "net"
  6. "net/http"
  7. "reflect"
  8. "strings"
  9. "time"
  10. "github.com/kataras/iris/v12/context"
  11. "github.com/kataras/iris/v12/sessions"
  12. "github.com/kataras/golog"
  13. )
  14. // Default is the default container value which can be used for dependencies share.
  15. var Default = New().WithLogger(golog.Default)
  16. // Container contains and delivers the Dependencies that will be binded
  17. // to the controller(s) or handler(s) that can be created
  18. // using the Container's `Handler` and `Struct` methods.
  19. //
  20. // This is not exported for being used by everyone, use it only when you want
  21. // to share containers between multi mvc.go#Application
  22. // or make custom hero handlers that can be used on the standard
  23. // iris' APIBuilder.
  24. //
  25. // For a more high-level structure please take a look at the "mvc.go#Application".
  26. type Container struct {
  27. // Optional Logger to report dependencies and matched bindings
  28. // per struct, function and method.
  29. // By default it is set by the Party creator of this Container.
  30. Logger *golog.Logger
  31. // Sorter specifies how the inputs should be sorted before binded.
  32. // Defaults to sort by "thinnest" target empty interface.
  33. Sorter Sorter
  34. // The dependencies entries.
  35. Dependencies []*Dependency
  36. // MarkExportedFieldsAsRequired reports whether all struct's fields
  37. // MUST be binded to a dependency from the `Dependencies` list field.
  38. // In-short, if it is set to true and if at least one exported field
  39. // of a struct is not binded to a dependency then
  40. // the entire application will exit with a panic message before server startup.
  41. MarkExportedFieldsAsRequired bool
  42. // DisablePayloadAutoBinding reports whether
  43. // a function's parameter or struct's field of struct type
  44. // should not be binded automatically to the request body (e.g. JSON)
  45. // if a dependency for that type is missing.
  46. // By default the binder will bind structs to request body,
  47. // set to true to disable that kind of behavior.
  48. DisablePayloadAutoBinding bool
  49. // DisableStructDynamicBindings if true panics on struct handler (controller)
  50. // if at least one input binding depends on the request and not in a static structure.
  51. DisableStructDynamicBindings bool
  52. // StructDependents if true then the Container will try to resolve
  53. // the fields of a struct value, if any, when it's a dependent struct value
  54. // based on the previous registered dependencies.
  55. //
  56. // Defaults to false.
  57. EnableStructDependents bool // this can be renamed to IndirectDependencies?.
  58. // DependencyMatcher holds the function that compares equality between
  59. // a dependency with an input. Defaults to DefaultMatchDependencyFunc.
  60. DependencyMatcher DependencyMatcher
  61. // GetErrorHandler should return a valid `ErrorHandler` to handle bindings AND handler dispatch errors.
  62. // Defaults to a functon which returns the `DefaultErrorHandler`.
  63. GetErrorHandler func(*context.Context) ErrorHandler // cannot be nil.
  64. // Reports contains an ordered list of information about bindings for further analysys and testing.
  65. Reports []*Report
  66. // resultHandlers is a list of functions that serve the return struct value of a function handler.
  67. // Defaults to "defaultResultHandler" but it can be overridden.
  68. resultHandlers []func(next ResultHandler) ResultHandler
  69. }
  70. // A Report holds meta information about dependency sources and target values per package,
  71. // struct, struct's fields, struct's method, package-level function or closure.
  72. // E.g. main -> (*UserController) -> HandleHTTPError.
  73. type Report struct {
  74. // The name is the last part of the name of a struct or its methods or a function.
  75. // Each name is splited by its package.struct.field or package.funcName or package.func.inlineFunc.
  76. Name string
  77. // If it's a struct or package or function
  78. // then it contains children reports of each one of its methods or input parameters
  79. // respectfully.
  80. Reports []*Report
  81. Parent *Report
  82. Entries []ReportEntry
  83. }
  84. // A ReportEntry holds the information about a binding.
  85. type ReportEntry struct {
  86. InputPosition int // struct field position or parameter position.
  87. InputFieldName string // if it's a struct field, then this is its type name (we can't get param names).
  88. InputFieldType reflect.Type // the input's type.
  89. DependencyValue interface{} // the dependency value binded to that InputPosition of Name.
  90. DependencyFile string // the file
  91. DependencyLine int // and line number of the dependency's value.
  92. Static bool
  93. }
  94. func (r *Report) fill(bindings []*binding) {
  95. for _, b := range bindings {
  96. inputFieldName := b.Input.StructFieldName
  97. if inputFieldName == "" {
  98. // it's not a struct field, then type.
  99. inputFieldName = b.Input.Type.String()
  100. }
  101. // remove only the main one prefix.
  102. inputFieldName = strings.TrimPrefix(inputFieldName, "main.")
  103. fieldName := inputFieldName
  104. switch fieldName {
  105. case "*context.Context":
  106. inputFieldName = strings.Replace(inputFieldName, "*context", "iris", 1)
  107. case "hero.Code", "hero.Result", "hero.View", "hero.Response":
  108. inputFieldName = strings.Replace(inputFieldName, "hero", "mvc", 1)
  109. }
  110. entry := ReportEntry{
  111. InputPosition: b.Input.Index,
  112. InputFieldName: inputFieldName,
  113. InputFieldType: b.Input.Type,
  114. DependencyValue: b.Dependency.OriginalValue,
  115. DependencyFile: b.Dependency.Source.File,
  116. DependencyLine: b.Dependency.Source.Line,
  117. Static: b.Dependency.Static,
  118. }
  119. r.Entries = append(r.Entries, entry)
  120. }
  121. }
  122. // fillReport adds a report to the Reports field.
  123. func (c *Container) fillReport(fullName string, bindings []*binding) {
  124. // r := c.getReport(fullName)
  125. r := &Report{
  126. Name: fullName,
  127. }
  128. r.fill(bindings)
  129. c.Reports = append(c.Reports, r)
  130. }
  131. // BuiltinDependencies is a list of builtin dependencies that are added on Container's initilization.
  132. // Contains the iris context, standard context, iris sessions and time dependencies.
  133. var BuiltinDependencies = []*Dependency{
  134. // iris context dependency.
  135. newDependency(func(ctx *context.Context) *context.Context { return ctx }, true, false, nil).Explicitly(),
  136. // standard context dependency.
  137. newDependency(func(ctx *context.Context) stdContext.Context {
  138. return ctx.Request().Context()
  139. }, true, false, nil).Explicitly(),
  140. // iris session dependency.
  141. newDependency(func(ctx *context.Context) *sessions.Session {
  142. session := sessions.Get(ctx)
  143. if session == nil {
  144. ctx.Application().Logger().Debugf("binding: session is nil\nMaybe inside HandleHTTPError? Register it with app.UseRouter(sess.Handler()) to fix it")
  145. // let's don't panic here and let the application continue, now we support
  146. // not matched routes inside the controller through HandleHTTPError,
  147. // so each dependency can check if session was not nil or just use `UseRouter` instead of `Use`
  148. // to register the sessions middleware.
  149. }
  150. return session
  151. }, true, false, nil).Explicitly(),
  152. // application's logger.
  153. newDependency(func(ctx *context.Context) *golog.Logger {
  154. return ctx.Application().Logger()
  155. }, true, false, nil).Explicitly(),
  156. // time.Time to time.Now dependency.
  157. newDependency(func(ctx *context.Context) time.Time {
  158. return time.Now()
  159. }, true, false, nil).Explicitly(),
  160. // standard http Request dependency.
  161. newDependency(func(ctx *context.Context) *http.Request {
  162. return ctx.Request()
  163. }, true, false, nil).Explicitly(),
  164. // standard http ResponseWriter dependency.
  165. newDependency(func(ctx *context.Context) http.ResponseWriter {
  166. return ctx.ResponseWriter()
  167. }, true, false, nil).Explicitly(),
  168. // http headers dependency.
  169. newDependency(func(ctx *context.Context) http.Header {
  170. return ctx.Request().Header
  171. }, true, false, nil).Explicitly(),
  172. // Client IP.
  173. newDependency(func(ctx *context.Context) net.IP {
  174. return net.ParseIP(ctx.RemoteAddr())
  175. }, true, false, nil).Explicitly(),
  176. // Status Code (special type for MVC HTTP Error handler to not conflict with path parameters)
  177. newDependency(func(ctx *context.Context) Code {
  178. return Code(ctx.GetStatusCode())
  179. }, true, false, nil).Explicitly(),
  180. // Context Error. May be nil
  181. newDependency(func(ctx *context.Context) Err {
  182. err := ctx.GetErr()
  183. if err == nil {
  184. return nil
  185. }
  186. return err
  187. }, true, false, nil).Explicitly(),
  188. // Context User, e.g. from basic authentication.
  189. newDependency(func(ctx *context.Context) context.User {
  190. u := ctx.User()
  191. if u == nil {
  192. return nil
  193. }
  194. return u
  195. }, true, false, nil),
  196. // payload and param bindings are dynamically allocated and declared at the end of the `binding` source file.
  197. }
  198. // New returns a new Container, a container for dependencies and a factory
  199. // for handlers and controllers, this is used internally by the `mvc#Application` structure.
  200. // Please take a look at the structure's documentation for more information.
  201. func New(dependencies ...interface{}) *Container {
  202. deps := make([]*Dependency, len(BuiltinDependencies))
  203. copy(deps, BuiltinDependencies)
  204. c := &Container{
  205. Sorter: sortByNumMethods,
  206. Dependencies: deps,
  207. GetErrorHandler: func(*context.Context) ErrorHandler {
  208. return DefaultErrorHandler
  209. },
  210. DependencyMatcher: DefaultDependencyMatcher,
  211. }
  212. for _, dependency := range dependencies {
  213. c.Register(dependency)
  214. }
  215. return c
  216. }
  217. // WithLogger injects a logger to use to debug dependencies and bindings.
  218. func (c *Container) WithLogger(logger *golog.Logger) *Container {
  219. c.Logger = logger
  220. return c
  221. }
  222. // Clone returns a new cloned container.
  223. // It copies the ErrorHandler, Dependencies and all Options from "c" receiver.
  224. func (c *Container) Clone() *Container {
  225. cloned := New()
  226. cloned.Logger = c.Logger
  227. cloned.GetErrorHandler = c.GetErrorHandler
  228. cloned.Sorter = c.Sorter
  229. cloned.DependencyMatcher = c.DependencyMatcher
  230. clonedDeps := make([]*Dependency, len(c.Dependencies))
  231. copy(clonedDeps, c.Dependencies)
  232. cloned.Dependencies = clonedDeps
  233. cloned.DisablePayloadAutoBinding = c.DisablePayloadAutoBinding
  234. cloned.DisableStructDynamicBindings = c.DisableStructDynamicBindings
  235. cloned.EnableStructDependents = c.EnableStructDependents
  236. cloned.MarkExportedFieldsAsRequired = c.MarkExportedFieldsAsRequired
  237. cloned.resultHandlers = c.resultHandlers
  238. // Reports are not cloned.
  239. return cloned
  240. }
  241. // Register adds a dependency.
  242. // The value can be a single struct value-instance or a function
  243. // which has one input and one output, that output type
  244. // will be binded to the handler's input argument, if matching.
  245. //
  246. // Usage:
  247. // - Register(loggerService{prefix: "dev"})
  248. // - Register(func(ctx iris.Context) User {...})
  249. // - Register(func(User) OtherResponse {...})
  250. func Register(dependency interface{}) *Dependency {
  251. return Default.Register(dependency)
  252. }
  253. // Register adds a dependency.
  254. // The value can be a single struct value or a function.
  255. // Follow the rules:
  256. // * <T>{structValue}
  257. // * func(accepts <T>) returns <D> or (<D>, error)
  258. // * func(accepts iris.Context) returns <D> or (<D>, error)
  259. // * func(accepts1 iris.Context, accepts2 *hero.Input) returns <D> or (<D>, error)
  260. //
  261. // A Dependency can accept a previous registered dependency and return a new one or the same updated.
  262. // * func(accepts1 <D>, accepts2 <T>) returns <E> or (<E>, error) or error
  263. // * func(acceptsPathParameter1 string, id uint64) returns <T> or (<T>, error)
  264. //
  265. // Usage:
  266. //
  267. // - Register(loggerService{prefix: "dev"})
  268. // - Register(func(ctx iris.Context) User {...})
  269. // - Register(func(User) OtherResponse {...})
  270. func (c *Container) Register(dependency interface{}) *Dependency {
  271. d := newDependency(dependency, c.DisablePayloadAutoBinding, c.EnableStructDependents, c.DependencyMatcher, c.Dependencies...)
  272. if d.DestType == nil {
  273. // prepend the dynamic dependency so it will be tried at the end
  274. // (we don't care about performance here, design-time)
  275. c.Dependencies = append([]*Dependency{d}, c.Dependencies...)
  276. } else {
  277. c.Dependencies = append(c.Dependencies, d)
  278. }
  279. return d
  280. }
  281. // UseResultHandler adds a result handler to the Container.
  282. // A result handler can be used to inject the returned struct value
  283. // from a request handler or to replace the default renderer.
  284. func (c *Container) UseResultHandler(handler func(next ResultHandler) ResultHandler) *Container {
  285. c.resultHandlers = append(c.resultHandlers, handler)
  286. return c
  287. }
  288. // Handler accepts a "handler" function which can accept any input arguments that match
  289. // with the Container's `Dependencies` and any output result; like string, int (string,int),
  290. // custom structs, Result(View | Response) and anything you can imagine.
  291. // It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application,
  292. // as middleware or as simple route handler or subdomain's handler.
  293. func Handler(fn interface{}) context.Handler {
  294. return Default.Handler(fn)
  295. }
  296. // Handler accepts a handler "fn" function which can accept any input arguments that match
  297. // with the Container's `Dependencies` and any output result; like string, int (string,int),
  298. // custom structs, Result(View | Response) and more.
  299. // It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application,
  300. // as middleware or as simple route handler or subdomain's handler.
  301. //
  302. // func(...<T>) iris.Handler
  303. //
  304. // - if <T> are all static dependencies then
  305. // there is no reflection involved at serve-time.
  306. //
  307. // func(pathParameter string, ...<T>)
  308. //
  309. // - one or more path parameters (e.g. :uid, :string, :int, :path, :uint64)
  310. // are automatically binded to the first input Go standard types (string, int, uint64 and e.t.c.)
  311. //
  312. // func(<T>) error
  313. //
  314. // - if a function returns an error then this error's text is sent to the client automatically.
  315. //
  316. // func(<T>) <R>
  317. //
  318. // - The result of the function is a dependency too.
  319. // If <R> is a request-scope dependency (dynamic) then
  320. // this function will be called at every request.
  321. //
  322. // func(<T>) <R>
  323. //
  324. // - If <R> is static dependency (e.g. a database or a service) then its result
  325. // can be used as a static dependency to the next dependencies or to the controller/function itself.
  326. func (c *Container) Handler(fn interface{}) context.Handler {
  327. return c.HandlerWithParams(fn, 0)
  328. }
  329. // HandlerWithParams same as `Handler` but it can receive a total path parameters counts
  330. // to resolve coblex path parameters input dependencies.
  331. func (c *Container) HandlerWithParams(fn interface{}, paramsCount int) context.Handler {
  332. return makeHandler(fn, c, paramsCount)
  333. }
  334. // Struct accepts a pointer to a struct value and returns a structure which
  335. // contains bindings for the struct's fields and a method to
  336. // extract a Handler from this struct's method.
  337. func (c *Container) Struct(ptrValue interface{}, partyParamsCount int) *Struct {
  338. return makeStruct(ptrValue, c, partyParamsCount)
  339. }
  340. // ErrMissingDependency may returned only from the `Container.Inject` method
  341. // when not a matching dependency found for "toPtr".
  342. var ErrMissingDependency = errors.New("missing dependency")
  343. // Inject SHOULD only be used outside of HTTP handlers (performance is not priority for this method)
  344. // as it does not pre-calculate the available list of bindings for the "toPtr" and the registered dependencies.
  345. //
  346. // It sets a static-only matching dependency to the value of "toPtr".
  347. // The parameter "toPtr" SHOULD be a pointer to a value corresponding to a dependency,
  348. // like input parameter of a handler or field of a struct.
  349. //
  350. // If no matching dependency found, the `Inject` method returns an `ErrMissingDependency` and
  351. // "toPtr" keeps its original state (e.g. nil).
  352. //
  353. // Example Code:
  354. // c.Register(&LocalDatabase{...})
  355. // [...]
  356. // var db Database
  357. // err := c.Inject(&db)
  358. func (c *Container) Inject(toPtr interface{}) error {
  359. val := reflect.Indirect(valueOf(toPtr))
  360. typ := val.Type()
  361. for _, d := range c.Dependencies {
  362. if d.Static && c.DependencyMatcher(d, typ) {
  363. v, err := d.Handle(nil, &Input{Type: typ})
  364. if err != nil {
  365. if err == ErrSeeOther {
  366. continue
  367. }
  368. return err
  369. }
  370. val.Set(v)
  371. return nil
  372. }
  373. }
  374. return ErrMissingDependency
  375. }