package router import ( "net/http" "github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/hero" "github.com/kataras/iris/v12/macro" ) // APIContainer is a wrapper of a common `Party` featured by Dependency Injection. // See `Party.ConfigureContainer` for more. type APIContainer struct { // Self returns the original `Party` without DI features. Self Party // Container is the per-party (and its children gets a clone) DI container.. Container *hero.Container } // Party returns a child of this `APIContainer` featured with Dependency Injection. // Like the `Self.Party` method does for the common Router Groups. func (api *APIContainer) Party(relativePath string, handlersFn ...interface{}) *APIContainer { handlers := api.convertHandlerFuncs(relativePath, handlersFn...) p := api.Self.Party(relativePath, handlers...) return p.ConfigureContainer() } // PartyFunc same as `Party` but it accepts a party builder function instead. // Returns the new Party's APIContainer func (api *APIContainer) PartyFunc(relativePath string, fn func(*APIContainer)) *APIContainer { childContainer := api.Party(relativePath) fn(childContainer) return childContainer } // OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers). // The "errorHandler" handles any error may occurred and returned // during dependencies injection of the Party's hero handlers or from the handlers themselves. // // Same as: // Container.GetErrorHandler = func(ctx iris.Context) hero.ErrorHandler { return errorHandler } // // See `RegisterDependency`, `Use`, `Done` and `Handle` too. func (api *APIContainer) OnError(errorHandler func(*context.Context, error)) { errHandler := hero.ErrorHandlerFunc(errorHandler) api.Container.GetErrorHandler = func(ctx *context.Context) hero.ErrorHandler { return errHandler } } // RegisterDependency adds a dependency. // The value can be a single struct value or a function. // Follow the rules: // * {structValue} // * func(accepts ) returns or (, error) // * func(accepts iris.Context) returns or (, error) // // A Dependency can accept a previous registered dependency and return a new one or the same updated. // * func(accepts1 , accepts2 ) returns or (, error) or error // * func(acceptsPathParameter1 string, id uint64) returns or (, error) // // Usage: // // - RegisterDependency(loggerService{prefix: "dev"}) // - RegisterDependency(func(ctx iris.Context) User {...}) // - RegisterDependency(func(User) OtherResponse {...}) // // See `OnError`, `Use`, `Done` and `Handle` too. func (api *APIContainer) RegisterDependency(dependency interface{}) *hero.Dependency { return api.Container.Register(dependency) } // UseResultHandler adds a result handler to the Container. // A result handler can be used to inject the returned struct value // from a request handler or to replace the default renderer. func (api *APIContainer) UseResultHandler(handler func(next hero.ResultHandler) hero.ResultHandler) *APIContainer { api.Container.UseResultHandler(handler) return api } // EnableStrictMode sets the container's DisablePayloadAutoBinding and MarkExportedFieldsAsRequired to true. // Meaning that all struct's fields (or function's parameters) should be binded manually (except the path parameters). // // Note that children will clone the same properties. // Call the same method with `false` for children // to enable automatic binding on missing dependencies. // // Strict mode is disabled by default; // structs or path parameters that don't match to registered dependencies // are automatically binded from the request context (body and url path parameters respectfully). func (api *APIContainer) EnableStrictMode(strictMode bool) *APIContainer { api.Container.DisablePayloadAutoBinding = strictMode api.Container.MarkExportedFieldsAsRequired = strictMode return api } // convertHandlerFuncs accepts Iris hero handlers and returns a slice of native Iris handlers. func (api *APIContainer) convertHandlerFuncs(relativePath string, handlersFn ...interface{}) context.Handlers { fullpath := api.Self.GetRelPath() + relativePath paramsCount := macro.CountParams(fullpath, *api.Self.Macros()) handlers := make(context.Handlers, 0, len(handlersFn)) for _, h := range handlersFn { handlers = append(handlers, api.Container.HandlerWithParams(h, paramsCount)) } // Note: let end-developer to decide that through Party.SetExecutionRules. // On that type of handlers the end-developer does not have to include the Context in the handler, // so the ctx.Next is automatically called unless an `ErrStopExecution` returned (implementation inside hero pkg). // // o := ExecutionOptions{Force: true} // o.apply(&handlers) return handlers } func fixRouteInfo(route *Route, handlersFn []interface{}) { // Fix main handler name and source modified by execution rules wrapper. route.MainHandlerName, route.MainHandlerIndex = context.MainHandlerName(handlersFn...) if len(handlersFn) > route.MainHandlerIndex { route.SourceFileName, route.SourceLineNumber = context.HandlerFileLineRel(handlersFn[route.MainHandlerIndex]) } } // Handler receives a function which can receive dependencies and output result // and returns a common Iris Handler, useful for Versioning API integration otherwise // the `Handle/Get/Post...` methods are preferable. func (api *APIContainer) Handler(handlerFn interface{}, handlerParamsCount int) context.Handler { paramsCount := macro.CountParams(api.Self.GetRelPath(), *api.Self.Macros()) + handlerParamsCount return api.Container.HandlerWithParams(handlerFn, paramsCount) } // Use same as `Self.Use` but it accepts dynamic functions as its "handlersFn" input. // // See `OnError`, `RegisterDependency`, `Done` and `Handle` for more. func (api *APIContainer) Use(handlersFn ...interface{}) { handlers := api.convertHandlerFuncs("/", handlersFn...) api.Self.Use(handlers...) } // Done same as `Self.Done` but it accepts dynamic functions as its "handlersFn" input. // See `OnError`, `RegisterDependency`, `Use` and `Handle` for more. func (api *APIContainer) Done(handlersFn ...interface{}) { handlers := api.convertHandlerFuncs("/", handlersFn...) api.Self.Done(handlers...) } // Handle same as `Self.Handle` but it accepts one or more "handlersFn" functions which each one of them // can accept any input arguments that match with the Party's registered Container's `Dependencies` and // any output result; like custom structs , string, []byte, int, error, // a combination of the above, hero.Result(hero.View | hero.Response) and more. // // It's common from a hero handler to not even need to accept a `Context`, for that reason, // the "handlersFn" will call `ctx.Next()` automatically when not called manually. // To stop the execution and not continue to the next "handlersFn" // the end-developer should output an error and return `iris.ErrStopExecution`. // // See `OnError`, `RegisterDependency`, `Use`, `Done`, `Get`, `Post`, `Put`, `Patch` and `Delete` too. func (api *APIContainer) Handle(method, relativePath string, handlersFn ...interface{}) *Route { handlers := api.convertHandlerFuncs(relativePath, handlersFn...) route := api.Self.Handle(method, relativePath, handlers...) fixRouteInfo(route, handlersFn) return route } // Get registers a route for the Get HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Get(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodGet, relativePath, handlersFn...) } // Post registers a route for the Post HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Post(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodPost, relativePath, handlersFn...) } // Put registers a route for the Put HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Put(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodPut, relativePath, handlersFn...) } // Delete registers a route for the Delete HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Delete(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodDelete, relativePath, handlersFn...) } // Connect registers a route for the Connect HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Connect(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodConnect, relativePath, handlersFn...) } // Head registers a route for the Head HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Head(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodHead, relativePath, handlersFn...) } // Options registers a route for the Options HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Options(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodOptions, relativePath, handlersFn...) } // Patch registers a route for the Patch HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Patch(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodPatch, relativePath, handlersFn...) } // Trace registers a route for the Trace HTTP Method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. func (api *APIContainer) Trace(relativePath string, handlersFn ...interface{}) *Route { return api.Handle(http.MethodTrace, relativePath, handlersFn...) } // Any registers a route for ALL of the HTTP methods: // Get // Post // Put // Delete // Head // Patch // Options // Connect // Trace func (api *APIContainer) Any(relativePath string, handlersFn ...interface{}) (routes []*Route) { handlers := api.convertHandlerFuncs(relativePath, handlersFn...) for _, m := range AllMethods { r := api.Self.HandleMany(m, relativePath, handlers...) routes = append(routes, r...) } return } /* TODO: fix those // OnErrorCode registers a handlers chain for this `Party` for a specific HTTP status code. // Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml // Look `OnAnyErrorCode` too. func (api *APIContainer) OnErrorCode(statusCode int, handlersFn ...interface{}) []*Route { handlers := api.convertHandlerFuncs("/{tail:path}", handlersFn...) return api.Self.OnErrorCode(statusCode, handlers...) } // OnAnyErrorCode registers a handlers chain for all error codes // (4xxx and 5xxx, change the `ClientErrorCodes` and `ServerErrorCodes` variables to modify those) // Look `OnErrorCode` too. func (api *APIContainer) OnAnyErrorCode(handlersFn ...interface{}) []*Route { handlers := api.convertHandlerFuncs("/{tail:path}", handlersFn...) return api.Self.OnAnyErrorCode(handlers...) } */