From 66e3c26efee0f7c7de2717f3f71d069eaf0eb025 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 21 Jan 2024 17:16:59 +0200 Subject: [PATCH] new app.MiddlewareExists method --- HISTORY.md | 36 ++++++++++++++++++++ context/handler.go | 62 +++++++++++++++++++++++++++-------- core/router/api_builder.go | 20 +++++++++++ core/router/party.go | 2 ++ iris_guide.go | 10 ++++-- middleware/recover/recover.go | 2 +- x/errors/errors.go | 6 ++++ x/errors/handlers.go | 4 +++ 8 files changed, 125 insertions(+), 17 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 23f2ad9e..958514aa 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -24,6 +24,42 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene Changes apply to `main` branch. +- New `Application/Party.MiddlewareExists(handlerNameOrHandler)` method added, example: + +```go +package main + +import ( + "fmt" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/x/errors" +) + +func main() { + app := iris.New() + + app.UseRouter(errors.RecoveryHandler) + + if app.MiddlewareExists(errors.RecoveryHandler) { // <- HERE. + fmt.Println("errors.RecoveryHandler exists") + } + // OR: + // if app.MiddlewareExists("iris.errors.recover") { + // fmt.Println("Iris.Errors.Recovery exists") + // } + + app.Get("/", indexHandler) + + app.Listen(":8080") +} + +func indexHandler(ctx iris.Context) { + panic("an error here") + ctx.HTML("

Index

") +} + +``` - New `x/errors.Intercept(func(ctx iris.Context, req *CreateRequest, resp *CreateResponse) error{ ... })` package-level function. ```go diff --git a/context/handler.go b/context/handler.go index 9e13df90..77566699 100644 --- a/context/handler.go +++ b/context/handler.go @@ -38,6 +38,7 @@ var ( "iris.logger", "iris.rate", "iris.methodoverride", + "iris.errors.recover", } ) @@ -111,20 +112,6 @@ type Handler = func(*Context) // See `Handler` for more. type Handlers = []Handler -// CopyHandlers returns a copy of "handlers" Handlers slice. -func CopyHandlers(handlers []Handler) Handlers { - handlersCp := make([]Handler, 0, len(handlers)) - for _, handler := range handlers { - if handler == nil { - continue - } - - handlersCp = append(handlersCp, handler) - } - - return handlersCp -} - func valueOf(v interface{}) reflect.Value { if val, ok := v.(reflect.Value); ok { return val @@ -378,3 +365,50 @@ reg: return h1 } + +// CopyHandlers returns a copy of "handlers" Handlers slice. +func CopyHandlers(handlers Handlers) Handlers { + handlersCp := make(Handlers, 0, len(handlers)) + for _, handler := range handlers { + if handler == nil { + continue + } + + handlersCp = append(handlersCp, handler) + } + + return handlersCp +} + +// HandlerExists reports whether a handler exists in the "handlers" slice. +func HandlerExists(handlers Handlers, handlerNameOrFunc any) bool { + if handlerNameOrFunc == nil { + return false + } + + var matchHandler func(any) bool + + switch v := handlerNameOrFunc.(type) { + case string: + matchHandler = func(handler any) bool { + return HandlerName(handler) == v + } + case Handler: + handlerName := HandlerName(v) + matchHandler = func(handler any) bool { + return HandlerName(handler) == handlerName + } + default: + matchHandler = func(handler any) bool { + return reflect.TypeOf(handler) == reflect.TypeOf(v) + } + } + + for _, handler := range handlers { + if matchHandler(handler) { + return true + } + } + + return false +} diff --git a/core/router/api_builder.go b/core/router/api_builder.go index 013b39d6..f9ef4ada 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -1370,6 +1370,26 @@ func (api *APIBuilder) DoneGlobal(handlers ...context.Handler) { api.doneGlobalHandlers = append(api.doneGlobalHandlers, handlers...) } +// MiddlewareExists reports whether the given handler exists in the middleware chain. +func (api *APIBuilder) MiddlewareExists(handlerNameOrFunc any) bool { + if handlerNameOrFunc == nil { + return false + } + + var handlers context.Handlers + + if filter, ok := api.routerFilters[api]; ok { + handlers = append(handlers, filter.Handlers...) + } + + handlers = append(handlers, api.middleware...) + handlers = append(handlers, api.doneHandlers...) + handlers = append(handlers, api.beginGlobalHandlers...) + handlers = append(handlers, api.doneGlobalHandlers...) + + return context.HandlerExists(handlers, handlerNameOrFunc) +} + // RemoveHandler deletes a handler from begin and done handlers // based on its name or the handler pc function. // Note that UseGlobal and DoneGlobal handlers cannot be removed diff --git a/core/router/party.go b/core/router/party.go index 6259cc3a..079fee9e 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -234,6 +234,8 @@ type Party 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) + // MiddlewareExists reports whether the given handler exists in the middleware chain. + MiddlewareExists(handlerNameOrFunc any) bool // RemoveHandler deletes a handler from begin and done handlers // based on its name or the handler pc function. // diff --git a/iris_guide.go b/iris_guide.go index 7d39f346..91252622 100644 --- a/iris_guide.go +++ b/iris_guide.go @@ -4,6 +4,7 @@ import ( "strings" "time" + "github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/core/router" "github.com/kataras/iris/v12/middleware/cors" @@ -508,8 +509,13 @@ func (s *step7) Build() *Application { app.SetContextErrorHandler(errors.DefaultContextErrorHandler) app.Macros().SetErrorHandler(errors.DefaultPathParameterTypeErrorHandler) - app.UseRouter(recover.New()) - app.UseRouter(s.step6.step5.routerMiddlewares...) + routeFilters := s.step6.step5.routerMiddlewares + if !context.HandlerExists(routeFilters, errors.RecoveryHandler) { + // If not errors.RecoveryHandler registered, then use the default one. + app.UseRouter(recover.New()) + } + + app.UseRouter(routeFilters...) app.UseRouter(func(ctx Context) { ctx.Header("Server", "Iris") if dev := s.step6.step5.step4.step3.developer; dev != "" { diff --git a/middleware/recover/recover.go b/middleware/recover/recover.go index f3d06864..2a640556 100644 --- a/middleware/recover/recover.go +++ b/middleware/recover/recover.go @@ -11,7 +11,7 @@ import ( ) func init() { - context.SetHandlerName("iris/middleware/recover.*", "iris.recover") + context.SetHandlerName("iris/middleware/recover.*", "iris.recover") // this name won't work because New() is a function that returns a handler. } // New returns a new recovery middleware, diff --git a/x/errors/errors.go b/x/errors/errors.go index 31d0c129..ba3801e6 100644 --- a/x/errors/errors.go +++ b/x/errors/errors.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/http" "github.com/kataras/iris/v12/context" @@ -20,6 +21,11 @@ type LogErrorFunc = func(ctx *context.Context, err error) // LogError can be modified to customize the way an error is logged to the server (most common: internal server errors, database errors et.c.). // Can be used to customize the error logging, e.g. using Sentry (cloud-based error console). var LogError LogErrorFunc = func(ctx *context.Context, err error) { + if ctx == nil { + slog.Error(err.Error()) + return + } + ctx.Application().Logger().Error(err) } diff --git a/x/errors/handlers.go b/x/errors/handlers.go index ee028121..aaf2ca07 100644 --- a/x/errors/handlers.go +++ b/x/errors/handlers.go @@ -13,6 +13,10 @@ import ( "golang.org/x/exp/constraints" ) +func init() { + context.SetHandlerName("iris/x/errors.RecoveryHandler.*", "iris.errors.recover") +} + // RecoveryHandler is a middleware which recovers from panics and sends an appropriate error response // to the logger and the client. func RecoveryHandler(ctx *context.Context) {