controller.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. package mvc
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. "github.com/kataras/iris/context"
  7. "github.com/kataras/iris/core/router"
  8. "github.com/kataras/iris/hero"
  9. "github.com/kataras/iris/macro"
  10. )
  11. // BaseController is the optional controller interface, if it's
  12. // completed by the end controller then the BeginRequest and EndRequest
  13. // are called between the controller's method responsible for the incoming request.
  14. type BaseController interface {
  15. BeginRequest(*context.Context)
  16. EndRequest(*context.Context)
  17. }
  18. type shared interface {
  19. Name() string
  20. Router() router.Party
  21. GetRoute(methodName string) *router.Route
  22. GetRoutes(methodName string) []*router.Route
  23. Handle(httpMethod, path, funcName string, middleware ...context.Handler) *router.Route
  24. HandleMany(httpMethod, path, funcName string, middleware ...context.Handler) []*router.Route
  25. }
  26. // BeforeActivation is being used as the only one input argument of a
  27. // `func(c *Controller) BeforeActivation(b mvc.BeforeActivation) {}`.
  28. //
  29. // It's being called before the controller's dependencies binding to the fields or the input arguments
  30. // but before server ran.
  31. //
  32. // It's being used to customize a controller if needed inside the controller itself,
  33. // it's called once per application.
  34. type BeforeActivation interface {
  35. shared
  36. Dependencies() *hero.Container
  37. }
  38. // AfterActivation is being used as the only one input argument of a
  39. // `func(c *Controller) AfterActivation(a mvc.AfterActivation) {}`.
  40. //
  41. // It's being called after the `BeforeActivation`,
  42. // and after controller's dependencies bind-ed to the fields or the input arguments but before server ran.
  43. //
  44. // It's being used to customize a controller if needed inside the controller itself,
  45. // it's called once per application.
  46. type AfterActivation interface {
  47. shared
  48. Singleton() bool
  49. DependenciesReadOnly() []*hero.Dependency
  50. }
  51. var (
  52. _ BeforeActivation = (*ControllerActivator)(nil)
  53. _ AfterActivation = (*ControllerActivator)(nil)
  54. )
  55. // ControllerActivator returns a new controller type info description.
  56. // Its functionality can be overridden by the end-dev.
  57. type ControllerActivator struct {
  58. app *Application
  59. injector *hero.Struct
  60. // initRef BaseController // the BaseController as it's passed from the end-dev.
  61. Value reflect.Value // the BaseController's Value.
  62. Type reflect.Type // raw type of the BaseController (initRef).
  63. // FullName it's the last package path segment + "." + the Name.
  64. // i.e: if login-example/user/controller.go, the FullName is "user.Controller".
  65. fullName string
  66. // the already-registered routes, key = the controller's function name.
  67. // End-devs can change some properties of the *Route on the `BeforeActivator` by using the
  68. // `GetRoute/GetRoutes(functionName)`.
  69. routes map[string][]*router.Route
  70. // BeginHandlers is a slice of middleware for this controller.
  71. // These handlers will be prependend to each one of
  72. // the route that this controller will register(Handle/HandleMany/struct methods)
  73. // to the targeted Party.
  74. // Look the `Use` method too.
  75. BeginHandlers context.Handlers
  76. // true if this controller listens and serves to websocket events.
  77. servesWebsocket bool
  78. // true to skip the internal "activate".
  79. activated bool
  80. }
  81. // NameOf returns the package name + the struct type's name,
  82. // it's used to take the full name of an Controller, the `ControllerActivator#Name`.
  83. func NameOf(v interface{}) string {
  84. elemTyp := indirectType(reflect.ValueOf(v).Type())
  85. typName := elemTyp.Name()
  86. pkgPath := elemTyp.PkgPath()
  87. fullname := pkgPath[strings.LastIndexByte(pkgPath, '/')+1:] + "." + typName
  88. return fullname
  89. }
  90. func newControllerActivator(app *Application, controller interface{}) *ControllerActivator {
  91. if controller == nil {
  92. return nil
  93. }
  94. if c, ok := controller.(*ControllerActivator); ok {
  95. return c
  96. }
  97. typ := reflect.TypeOf(controller)
  98. c := &ControllerActivator{
  99. // give access to the Router to the end-devs if they need it for some reason,
  100. // i.e register done handlers.
  101. app: app,
  102. Value: reflect.ValueOf(controller),
  103. Type: typ,
  104. // the full name of the controller: its type including the package path.
  105. fullName: NameOf(controller),
  106. // set some methods that end-dev cann't use accidentally
  107. // to register a route via the `Handle`,
  108. // all available exported and compatible methods
  109. // are being appended to the slice at the `parseMethods`,
  110. // if a new method is registered via `Handle` its function name
  111. // is also appended to that slice.
  112. routes: whatReservedMethods(typ),
  113. }
  114. return c
  115. }
  116. func whatReservedMethods(typ reflect.Type) map[string][]*router.Route {
  117. methods := []string{"BeforeActivation", "AfterActivation"}
  118. // BeforeActivatior/AfterActivation are not routes but they are
  119. // reserved names*
  120. if isBaseController(typ) {
  121. methods = append(methods, "BeginRequest", "EndRequest")
  122. }
  123. routes := make(map[string][]*router.Route, len(methods))
  124. for _, m := range methods {
  125. routes[m] = []*router.Route{}
  126. }
  127. return routes
  128. }
  129. // Name returns the full name of the controller, its package name + the type name.
  130. // Can used at both `BeforeActivation` and `AfterActivation`.
  131. func (c *ControllerActivator) Name() string {
  132. return c.fullName
  133. }
  134. // Router is the standard Iris router's public API.
  135. // With this you can register middleware, view layouts, subdomains, serve static files
  136. // and even add custom standard iris handlers as normally.
  137. //
  138. // This Router is the router instance that came from the parent MVC Application,
  139. // it's the `app.Party(...)` argument.
  140. //
  141. // Can used at both `BeforeActivation` and `AfterActivation`.
  142. func (c *ControllerActivator) Router() router.Party {
  143. return c.app.Router
  144. }
  145. // GetRoute returns the first registered route based on the controller's method name.
  146. // It can be used to change the route's name, which is useful for reverse routing
  147. // inside views. Custom routes can be registered with `Handle`, which returns the *Route.
  148. // This method exists mostly for the automatic method parsing based on the known patterns
  149. // inside a controller.
  150. //
  151. // A check for `nil` is necessary for unregistered methods.
  152. //
  153. // See `GetRoutes` and `Handle` too.
  154. func (c *ControllerActivator) GetRoute(methodName string) *router.Route {
  155. routes := c.GetRoutes(methodName)
  156. if len(routes) > 0 {
  157. return routes[0]
  158. }
  159. return nil
  160. }
  161. // GetRoutes returns one or more registered route based on the controller's method name.
  162. // It can be used to change the route's name, which is useful for reverse routing
  163. // inside views. Custom routes can be registered with `Handle`, which returns the *Route.
  164. // This method exists mostly for the automatic method parsing based on the known patterns
  165. // inside a controller.
  166. //
  167. // A check for `nil` is necessary for unregistered methods.
  168. //
  169. // See `Handle` too.
  170. func (c *ControllerActivator) GetRoutes(methodName string) []*router.Route {
  171. for name, routes := range c.routes {
  172. if name == methodName {
  173. return routes
  174. }
  175. }
  176. return nil
  177. }
  178. // Use registers a middleware for this Controller.
  179. // It appends one or more handlers to the `BeginHandlers`.
  180. // It's like the `Party.Use` but specifically
  181. // for the routes that this controller will register to the targeted `Party`.
  182. func (c *ControllerActivator) Use(handlers ...context.Handler) *ControllerActivator {
  183. c.BeginHandlers = append(c.BeginHandlers, handlers...)
  184. return c
  185. }
  186. // Singleton returns new if all incoming clients' requests
  187. // have the same controller instance.
  188. // This is done automatically by iris to reduce the creation
  189. // of a new controller on each request, if the controller doesn't contain
  190. // any unexported fields and all fields are services-like, static.
  191. func (c *ControllerActivator) Singleton() bool {
  192. if c.injector == nil {
  193. panic("MVC: Singleton called from wrong state the API gives access to it only `AfterActivation`, report this as bug")
  194. }
  195. return c.injector.Singleton
  196. }
  197. // DependenciesReadOnly returns a list of dependencies, including the controller's one.
  198. func (c *ControllerActivator) DependenciesReadOnly() []*hero.Dependency {
  199. if c.injector == nil {
  200. panic("MVC: DependenciesReadOnly called from wrong state the API gives access to it only `AfterActivation`, report this as bug")
  201. }
  202. return c.injector.Container.Dependencies
  203. }
  204. // Dependencies returns a value which can manage the controller's dependencies.
  205. func (c *ControllerActivator) Dependencies() *hero.Container {
  206. return c.app.container
  207. }
  208. // checks if a method is already registered.
  209. func (c *ControllerActivator) isReservedMethod(name string) bool {
  210. for methodName := range c.routes {
  211. if methodName == name {
  212. return true
  213. }
  214. }
  215. return false
  216. }
  217. func (c *ControllerActivator) isReservedMethodHandler(method, path string) bool {
  218. for _, routes := range c.routes {
  219. for _, r := range routes {
  220. if r.Method == method && r.Path == path {
  221. return true
  222. }
  223. }
  224. }
  225. return false
  226. }
  227. func (c *ControllerActivator) markAsWebsocket() {
  228. c.servesWebsocket = true
  229. c.attachInjector()
  230. }
  231. func (c *ControllerActivator) attachInjector() {
  232. if c.injector == nil {
  233. partyCountParams := macro.CountParams(c.app.Router.GetRelPath(), *c.app.Router.Macros())
  234. c.injector = c.app.container.Struct(c.Value, partyCountParams)
  235. }
  236. }
  237. // Activated can be called to skip the internal method parsing.
  238. func (c *ControllerActivator) Activated() bool {
  239. b := c.activated
  240. c.activated = true
  241. return b
  242. }
  243. func (c *ControllerActivator) activate() {
  244. if c.Activated() {
  245. return
  246. }
  247. c.parseMethods()
  248. }
  249. // register all available, exported methods to handlers if possible.
  250. func (c *ControllerActivator) parseMethods() {
  251. n := c.Type.NumMethod()
  252. for i := 0; i < n; i++ {
  253. m := c.Type.Method(i)
  254. c.parseMethod(m)
  255. }
  256. }
  257. func (c *ControllerActivator) parseMethod(m reflect.Method) {
  258. httpMethod, httpPath, err := parseMethod(c.app.Router.Macros(), m, c.isReservedMethod)
  259. if err != nil {
  260. if err != errSkip {
  261. c.addErr(fmt.Errorf("MVC: fail to parse the route path and HTTP method for '%s.%s': %v", c.fullName, m.Name, err))
  262. }
  263. return
  264. }
  265. c.Handle(httpMethod, httpPath, m.Name)
  266. }
  267. func (c *ControllerActivator) addErr(err error) bool {
  268. return c.app.Router.GetReporter().Err(err) != nil
  269. }
  270. // Handle registers a route based on a http method, the route's path
  271. // and a function name that belongs to the controller, it accepts
  272. // a forth, optionally, variadic parameter which is the before handlers.
  273. //
  274. // Just like `Party#Handle`, it returns the `*router.Route`, if failed
  275. // then it logs the errors and it returns nil, you can check the errors
  276. // programmatically by the `Party#GetReporter`.
  277. //
  278. // Handle will add a route to the "funcName".
  279. func (c *ControllerActivator) Handle(method, path, funcName string, middleware ...context.Handler) *router.Route {
  280. routes := c.handleMany(method, path, funcName, false, middleware...)
  281. if len(routes) == 0 {
  282. return nil
  283. }
  284. return routes[0]
  285. }
  286. // HandleMany like `Handle` but can register more than one path and HTTP method routes
  287. // separated by whitespace on the same controller's method.
  288. // Keep note that if the controller's method input arguments are path parameters dependencies
  289. // they should match with each of the given paths.
  290. //
  291. // Just like `Party#HandleMany`:, it returns the `[]*router.Routes`.
  292. // Usage:
  293. // func (*Controller) BeforeActivation(b mvc.BeforeActivation) {
  294. // b.HandleMany("GET", "/path /path1" /path2", "HandlePath")
  295. // }
  296. // HandleMany will override any routes of this "funcName".
  297. func (c *ControllerActivator) HandleMany(method, path, funcName string, middleware ...context.Handler) []*router.Route {
  298. return c.handleMany(method, path, funcName, true, middleware...)
  299. }
  300. func (c *ControllerActivator) handleMany(method, path, funcName string, override bool, middleware ...context.Handler) []*router.Route {
  301. if method == "" || path == "" || funcName == "" ||
  302. (c.isReservedMethod(funcName) && c.isReservedMethodHandler(method, path)) {
  303. // isReservedMethod -> if it's already registered
  304. // by a previous Handle or analyze methods internally.
  305. return nil
  306. }
  307. handler := c.handlerOf(path, funcName)
  308. middleware = context.JoinHandlers(c.BeginHandlers, middleware)
  309. // register the handler now.
  310. routes := c.app.Router.HandleMany(method, path, append(middleware, handler)...)
  311. if routes == nil {
  312. c.addErr(fmt.Errorf("MVC: unable to register a route for the path for '%s.%s'", c.fullName, funcName))
  313. return nil
  314. }
  315. for _, r := range routes {
  316. // change the main handler's name and file:line
  317. // in order to respect the controller's and give
  318. // a proper debug/log message.
  319. r.Description = "controller"
  320. r.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName)
  321. if m, ok := c.Type.MethodByName(funcName); ok {
  322. r.SourceFileName, r.SourceLineNumber = context.HandlerFileLineRel(m.Func)
  323. }
  324. }
  325. // add this as a reserved method name in order to
  326. // be sure that the same route
  327. // (method is allowed to be registered more than one on different routes - v11.2).
  328. existingRoutes, exist := c.routes[funcName]
  329. if override || !exist {
  330. c.routes[funcName] = routes
  331. } else {
  332. c.routes[funcName] = append(existingRoutes, routes...)
  333. }
  334. return routes
  335. }
  336. func (c *ControllerActivator) handlerOf(relPath, methodName string) context.Handler {
  337. c.attachInjector()
  338. fullpath := c.app.Router.GetRelPath() + relPath
  339. paramsCount := macro.CountParams(fullpath, *c.app.Router.Macros())
  340. handler := c.injector.MethodHandler(methodName, paramsCount)
  341. if isBaseController(c.Type) {
  342. return func(ctx *context.Context) {
  343. ctrl, err := c.injector.Acquire(ctx)
  344. if err != nil {
  345. // if err != hero.ErrStopExecution {
  346. // c.injector.Container.GetErrorHandler(ctx).HandleError(ctx, err)
  347. // }
  348. c.injector.Container.GetErrorHandler(ctx).HandleError(ctx, err)
  349. // allow skipping struct field bindings
  350. // errors by a custom error handler.
  351. if ctx.IsStopped() {
  352. return
  353. }
  354. }
  355. b := ctrl.Interface().(BaseController)
  356. // init the request.
  357. b.BeginRequest(ctx)
  358. // if begin request stopped the execution.
  359. if ctx.IsStopped() {
  360. return
  361. }
  362. handler(ctx)
  363. b.EndRequest(ctx)
  364. }
  365. }
  366. return handler
  367. }