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) {