From 2756435446870635a333d810b38166d590feff9e Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 1 Mar 2020 07:07:27 +0200 Subject: [PATCH] :rocket: add Party.OnErrorFunc, UseFunc and DoneFunc to be aligned with the new HandleFunc Former-commit-id: 381ce0f86f376ef7652b5e918474f9e37b391781 --- HISTORY.md | 65 +++++++++++++++++++++++++++++++++++++- core/router/api_builder.go | 62 +++++++++++++++++++++++++++++------- core/router/party.go | 24 ++++++++++++-- 3 files changed, 136 insertions(+), 15 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b0623204..6e98d367 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -70,7 +70,70 @@ func main() { } ``` -Your eyes don't lie you. You read well, no `ctx.ReadJSON(&v)` and `ctx.JSON(send)` neither `error` handling are presented. It is a huge relief but don't worry you can still control everything if you ever need, even errors from dependencies. Any error may occur from request-scoped dependencies or your own handler is dispatched through `Party.GetContainer().GetErrorHandler` which defaults to the `hero.DefaultErrorHandler` which sends a `400 Bad Request` response with the error's text as its body contents. If you want to handle `testInput` otherwise then just add a `Party.RegisterDependency(func(ctx iris.Context) testInput {...})` and you are ready to go. +Your eyes don't lie you. You read well, no `ctx.ReadJSON(&v)` and `ctx.JSON(send)` neither `error` handling are presented. It is a huge relief but don't worry you can still control everything if you ever need, even errors from dependencies. Any error may occur from request-scoped dependencies or your own handler is dispatched through `Party.GetContainer().GetErrorHandler` which defaults to the `hero.DefaultErrorHandler` which sends a `400 Bad Request` response with the error's text as its body contents, you can change it through `Party#OnErrorFunc`. If you want to handle `testInput` otherwise then just add a `Party.RegisterDependency(func(ctx iris.Context) testInput {...})` and you are ready to go. Here is a quick list of the new Party's methods: + +```go +// GetContainer returns the DI Container of this Party. +// Use it to manually convert functions or structs(controllers) to a Handler. +// +// See `OnErrorFunc`, `RegisterDependency`, `UseFunc`, `DoneFunc` and `HandleFunc` too. +GetContainer() *hero.Container +``` +```go +// OnErrorFunc 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: +// GetContainer().GetErrorHandler = func(ctx iris.Context) hero.ErrorHandler { return errorHandler } +// +// See `RegisterDependency`, `UseFunc`, `DoneFunc` and `HandleFunc` too. +OnErrorFunc(errorHandler func(context.Context, error)) +``` +```go +// 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) +// * func(accepts1 iris.Context, accepts2 *hero.Input) 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 `OnErrorFunc`, `UseFunc`, `DoneFunc` and `HandleFunc` too. +RegisterDependency(dependency interface{}) +``` +```go +// UseFunc same as "Use" but it accepts dynamic functions as its "handlersFn" input. +// See `OnErrorFunc`, `RegisterDependency`, `DoneFunc` and `HandleFunc` for more. +UseFunc(handlersFn ...interface{}) +// DoneFunc same as "Done" but it accepts dynamic functions as its "handlersFn" input. +// See `OnErrorFunc`, `RegisterDependency`, `UseFunc` and `HandleFunc` for more. +DoneFunc(handlersFn ...interface{}) +``` +```go +// HandleFunc same as `HandleFunc` 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 `OnErrorFunc`, `RegisterDependency`, `UseFunc` and `DoneFunc` too. +HandleFunc(method, relativePath string, handlersFn ...interface{}) *Route +``` Other Improvements: diff --git a/core/router/api_builder.go b/core/router/api_builder.go index 54143ae6..0076f8a9 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -252,11 +252,26 @@ func (api *APIBuilder) SetRegisterRule(rule RouteRegisterRule) Party { // GetContainer returns the DI Container of this Party. // Use it to manually convert functions or structs(controllers) to a Handler. // -// See `RegisterDependency` and `HandleFunc` too. +// See `OnErrorFunc`, `RegisterDependency`, `UseFunc`, `DoneFunc` and `HandleFunc` too. func (api *APIBuilder) GetContainer() *hero.Container { return api.container } +// OnErrorFunc 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: +// GetContainer().GetErrorHandler = func(ctx iris.Context) hero.ErrorHandler { return errorHandler } +// +// See `RegisterDependency`, `UseFunc`, `DoneFunc` and `HandleFunc` too. +func (api *APIBuilder) OnErrorFunc(errorHandler func(context.Context, error)) { + errHandler := hero.ErrorHandlerFunc(errorHandler) + api.GetContainer().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: @@ -274,20 +289,14 @@ func (api *APIBuilder) GetContainer() *hero.Container { // - RegisterDependency(loggerService{prefix: "dev"}) // - RegisterDependency(func(ctx iris.Context) User {...}) // - RegisterDependency(func(User) OtherResponse {...}) +// +// See `OnErrorFunc`, `UseFunc`, `DoneFunc` and `HandleFunc` too. func (api *APIBuilder) RegisterDependency(dependency interface{}) *hero.Dependency { return api.container.Register(dependency) } -// HandleFunc 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`. -func (api *APIBuilder) HandleFunc(method, relativePath string, handlersFn ...interface{}) *Route { +// convertHandlerFuncs accepts Iris hero handlers and returns a slice of native Iris handlers. +func (api *APIBuilder) convertHandlerFuncs(handlersFn ...interface{}) context.Handlers { handlers := make(context.Handlers, 0, len(handlersFn)) for _, h := range handlersFn { handlers = append(handlers, api.container.Handler(h)) @@ -298,6 +307,22 @@ func (api *APIBuilder) HandleFunc(method, relativePath string, handlersFn ...int o := ExecutionOptions{Force: true} o.apply(&handlers) + return handlers +} + +// HandleFunc same as `HandleFunc` 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 `OnErrorFunc`, `RegisterDependency`, `UseFunc` and `DoneFunc` too. +func (api *APIBuilder) HandleFunc(method, relativePath string, handlersFn ...interface{}) *Route { + handlers := api.convertHandlerFuncs(handlersFn...) return api.Handle(method, relativePath, handlers...) } @@ -714,6 +739,14 @@ func (api *APIBuilder) Use(handlers ...context.Handler) { api.middleware = append(api.middleware, handlers...) } +// UseFunc same as "Use" but it accepts dynamic functions as its "handlersFn" input. +// +// See `OnErrorFunc`, `RegisterDependency`, `DoneFunc` and `HandleFunc` for more. +func (api *APIBuilder) UseFunc(handlersFn ...interface{}) { + handlers := api.convertHandlerFuncs(handlersFn...) + api.Use(handlers...) +} + // UseGlobal registers handlers that should run at the very beginning. // It prepends those handler(s) to all routes, // including all parties, subdomains. @@ -740,6 +773,13 @@ func (api *APIBuilder) Done(handlers ...context.Handler) { api.doneHandlers = append(api.doneHandlers, handlers...) } +// DoneFunc same as "Done" but it accepts dynamic functions as its "handlersFn" input. +// See `OnErrorFunc`, `RegisterDependency`, `UseFunc` and `HandleFunc` for more. +func (api *APIBuilder) DoneFunc(handlersFn ...interface{}) { + handlers := api.convertHandlerFuncs(handlersFn...) + api.Done(handlers...) +} + // DoneGlobal registers handlers that should run at the very end. // It appends those handler(s) to all routes, // including all parties, subdomains. diff --git a/core/router/party.go b/core/router/party.go index a8490f5e..a083d292 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -57,10 +57,15 @@ type Party interface { // Use appends Handler(s) to the current Party's routes and child routes. // If the current Party is the root, then it registers the middleware to all child Parties' routes too. Use(middleware ...context.Handler) - + // UseFunc same as "Use" but it accepts dynamic functions as its "handlersFn" input. + // See `OnErrorFunc`, `RegisterDependency`, `DoneFunc` and `HandleFunc` for more. + UseFunc(handlersFn ...interface{}) // Done appends to the very end, Handler(s) to the current Party's routes and child routes. // The difference from .Use is that this/or these Handler(s) are being always running last. Done(handlers ...context.Handler) + // DoneFunc same as "Done" but it accepts dynamic functions as its "handlersFn" input. + // See `OnErrorFunc`, `RegisterDependency`, `UseFunc` and `HandleFunc` for more. + DoneFunc(handlersFn ...interface{}) // Reset removes all the begin and done handlers that may derived from the parent party via `Use` & `Done`, // and the execution rules. // Note that the `Reset` will not reset the handlers that are registered via `UseGlobal` & `DoneGlobal`. @@ -106,8 +111,17 @@ type Party interface { // GetContainer returns the DI Container of this Party. // Use it to manually convert functions or structs(controllers) to a Handler. // - // See `RegisterDependency` and `HandleFunc` too. + // See `OnErrorFunc`, `RegisterDependency`, `UseFunc`, `DoneFunc` and `HandleFunc` too. GetContainer() *hero.Container + // OnErrorFunc 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: + // GetContainer().GetErrorHandler = func(ctx iris.Context) hero.ErrorHandler { return errorHandler } + // + // See `RegisterDependency`, `UseFunc`, `DoneFunc` and `HandleFunc` too. + OnErrorFunc(errorHandler func(context.Context, error)) // RegisterDependency adds a dependency. // The value can be a single struct value or a function. // Follow the rules: @@ -125,8 +139,10 @@ type Party interface { // - RegisterDependency(loggerService{prefix: "dev"}) // - RegisterDependency(func(ctx iris.Context) User {...}) // - RegisterDependency(func(User) OtherResponse {...}) + // + // See `OnErrorFunc`, `UseFunc`, `DoneFunc` and `HandleFunc` too. RegisterDependency(dependency interface{}) *hero.Dependency - // HandleFunc accepts one or more "handlersFn" functions which each one of them + // HandleFunc same as `HandleFunc` 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. @@ -135,6 +151,8 @@ type Party interface { // 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 `OnErrorFunc`, `RegisterDependency`, `UseFunc` and `DoneFunc` too. HandleFunc(method, relativePath string, handlersFn ...interface{}) *Route // Handle registers a route to the server's router.