<!--
  Middleware
  From the Iris book, chapter 7, covering Iris 14.0.0 on Go 1.27.
  Import path: github.com/kataras/iris/v14
  Canonical page: https://iris-go.com/docs/middleware/
-->
# Chapter 7: Middleware

Iris has exactly one handler type, `func(ctx iris.Context)`, and middleware
is nothing more than a handler that decides whether the rest of the chain
runs. That single decision (call `ctx.Next()` or stop) powers
authentication guards, request logging, panic recovery and everything in
between. This chapter explains the handler chain, the registration methods
and their exact scopes, how to write your own middleware, and the ordering
rules that determine what runs when. The catalog of ready-made middleware
packages lives in [Chapter 12](https://iris-go.com/docs/built-in-middleware/); this chapter is
about the machinery they all share.

## Background

Every non-trivial web service repeats certain work on every request:
write an access log line, check credentials, attach an ID for tracing,
reject oversized bodies, recover from crashes. These are cross-cutting
concerns: work that belongs to no single endpoint but applies across
all of them. Copying that work into every handler multiplies it by the
number of endpoints and guarantees the copies drift apart; the login
check that was fixed in nine handlers and forgotten in the tenth is a
classic security hole.

Middleware is the pipeline answer to that problem. Instead of each
handler doing everything, the framework assembles a chain of small
functions around every request; each one does its piece and passes
control onward. Think of the layers of an onion: the request travels
inward through logging, authentication and limits before reaching the
handler at the core, and the response travels back out through the same
layers in reverse, which is why a timing middleware can note the clock
on the way in and compute the duration on the way out. You write each
concern once, register it once, and every route beneath it is covered.

The chain has a second power: any layer can stop the request. An
authentication guard that finds no valid token writes a 401 and simply
does not pass control on: the handler never runs, and neither does any
code that assumed a valid user. Ordering follows from the same logic:
the cheap protective layers (rate limits, body caps) belong outside the
expensive ones.

One distinction in this chapter deserves a plain definition up front.
Routing is the step where the framework matches a request's method and
path against the registered routes to pick a handler; the component
doing the matching is called the mux (short for multiplexer). "Before
routing" middleware (`UseRouter`) runs before that lookup, so it sees
every request, including those that match nothing and will end as a
404\. "Per route" middleware (`Use` and friends) is baked into each
matched route's chain, so it runs only when routing found a handler.
Practically: access logs and request IDs belong before routing, because
a request that matches nothing still deserves a log line; a guard that
loads the current user belongs on the routes that need a user.

## Handlers and the Chain

Every request runs through a chain of handlers assembled for its route.
Each handler receives the same `iris.Context` and controls the chain with
three moves:

```go
func example(ctx iris.Context) {
    // 1. Work before the rest of the chain.

    ctx.Next() // 2. Run the remaining handlers.

    // 3. Work after the chain below has completed
    //    (post-processing: timing, logging, metrics).
}
```

Not calling `ctx.Next()` ends the chain quietly: the following handlers
simply never run. `ctx.StopExecution()` goes further: it marks the
context as stopped, so any later `Next` call anywhere in the chain becomes
a no-op. `ctx.IsStopped()` reports that state. The `StopWith*` shortcuts
combine stopping with a response:

- `ctx.StopWithStatus(code)`: status code only.
- `ctx.StopWithText(code, text)`: plain text body.
- `ctx.StopWithJSON(code, value)`: JSON body.
- `ctx.StopWithError(code, err)`: records the error and writes its
  message, or only the status when the error is private.
- `ctx.StopWithPlainError(code, err)`: records the error without writing
  a body, leaving rendering to an error handler such as the `rest/errors`
  pipeline ([Chapter 9](https://iris-go.com/docs/error-handling/)).

A "middleware" is a handler registered before others: by position in the
route line, or through one of the registration methods below. There is no
separate middleware interface to implement.

## The Registration Family

Where you register a handler determines which requests it sees. The
methods, with their exact scopes as implemented in
`core/router/router_builder.go`:

| Call                     | Runs for                                                                 |
| ------------------------ | ------------------------------------------------------------------------ |
| `app.UseRouter(h)`       | Every request, before routing, including 404/405                         |
| `r.UseRouter(h)` (child) | Every request under the child's path prefix or subdomain, before routing |
| `app.Use(h)` (root)      | Every matched route, plus the 404 and 405 chains                         |
| `r.Use(h)` (child)       | Matched routes of that child and its children only                       |
| `app.UseRelevant(h)`     | Matched routes only, never 404/405                                       |
| `app.UseOnce(h)`         | Like root `Use`, but insert-or-replace                                   |
| `app.UseRelevantOnce(h)` | Like `UseRelevant`, but insert-or-replace                                |

One caveat on the 405 rows: the no-method chain only runs when you set
`Router.FireMethodNotAllowed`, which defaults to false. Until you do, a
wrong-method request goes to the 404 chain instead.

### UseRouter: before routing

`UseRouter` handlers run before the mux looks at the path. They fire for
every request (matched, not found, or method not allowed), which makes
them the right place for access logging, request IDs, CORS, compression
and rate limiting. Because they run first, they also hold veto power: a
`UseRouter` handler that writes a response without calling `ctx.Next()`
prevents routing from ever happening.

CORS (Cross-Origin Resource Sharing) shows why "every request" matters.
It is the browser mechanism that decides whether a page on one site may
call your API on another; before certain cross-site requests the browser
sends an automatic `OPTIONS` "preflight" request asking for permission.
A preflight often matches no registered route, so a CORS handler
attached per route would never see it. Registered with `UseRouter`, it
answers the preflight before routing can 404 it.

Called on a child router, `UseRouter` still registers at the top level but
wraps the handler in a prefix (and subdomain) check, so it fires only for
requests under that child's path, still before routing.

### Use: root versus child

The root-versus-child difference in `Use` is the one that surprises
people. On the root application, `Use` handlers join every route's chain
*and* are prepended to the no-route (404) and no-method (405) chains, so
they observe requests that match nothing at all. On a child router, `Use`
delegates to `UseRelevant`: the handlers join only the routes registered
under that child.

```go
package main

import (
    "github.com/kataras/iris/v14"
)

func main() {
    app := iris.New()

    app.UseRouter(func(ctx iris.Context) {
        // Before routing: fires for "/", "/admin/panel"
        // and any 404 or 405 request.
        ctx.Next()
    })

    app.Use(func(ctx iris.Context) {
        // Root Use: all matched routes, plus the 404/405 chains.
        ctx.Next()
    })

    admin := app.Router("/admin")
    admin.Use(func(ctx iris.Context) {
        // Child Use: only routes registered under /admin.
        ctx.Next()
    })
    admin.Get("/panel", func(ctx iris.Context) {
        ctx.Text("panel")
    })

    app.Get("/", func(ctx iris.Context) {
        ctx.Text("index")
    })

    app.Listen(":8080")
}
```

When you want root middleware that skips 404s, say so explicitly with
`app.UseRelevant(h)`. Note that `UseRelevant` and `UseRelevantOnce` are
methods of the application; the `iris.Router` interface that child groups
and `Routing(r iris.Router)` controllers receive exposes `Use`, `UseOnce`
and `UseRouter`. A child's `Use` is already matched-only.

### UseOnce and UseRelevantOnce: upsert

`UseOnce` and `UseRelevantOnce` insert a handler or, if a handler with the
same identity is already registered, replace it in place. Identity is the
function's resolved name, so re-running setup code does not stack
duplicates: the second registration replaces the first. The same rule
means two closures produced by the same factory function share a name and
therefore replace each other; register factory-produced middleware with
plain `Use` when you need several instances.

### WrapRouter: below everything

For integrations that need raw `net/http` semantics, `app.WrapRouter`
wraps the entire router:

```go
app.WrapRouter(func(w http.ResponseWriter, r *http.Request,
    router http.HandlerFunc) {
    // Runs before the Iris context even exists.
    router(w, r) // call the wrapped router (or don't).
})
```

Wrappers registered with `WrapRouter` execute last-registered-first;
`app.AddRouterWrapper` is the first-registered-first variant. Prefer
`UseRouter` unless a third-party package hands you an `http.Handler`
wrapper you cannot avoid.

## Guards and Per-Route Middleware

Handlers listed before the final one in a route registration act as
middleware for that route alone:

```go
app.Get("/reports", requireAuth, reportsHandler)
```

In controller-style APIs this becomes the guard idiom: guards are methods
on the API struct, so they see the same injected dependencies as the
handlers. A *group guard* registered with `r.Use` protects every route of
the controller; a *positional guard* between path and handler protects
individual routes. From the Bookshelf application of the framework's
`TUTORIAL.md`:

```go
// BooksAPI serves /shelves/{shelf_id:uuid}/books.
func (api *BooksAPI) Routing(r iris.Router) {
    // Group guard: every book route needs an existing shelf.
    r.Use(api.requireShelf)

    r.Get("/", api.list)
    // Positional guard: writes additionally need an unlocked shelf.
    r.Post("/", api.requireUnlocked, api.create)
    r.Put("/{id:uuid}", api.requireUnlocked, api.update)
    r.Delete("/{id:uuid}", api.requireUnlocked, api.delete)
}

// requireUnlocked rejects writes against a locked shelf. The shelf was
// loaded and cached by requireShelf, the group guard above.
func (api *BooksAPI) requireUnlocked(ctx iris.Context) {
    shelf, _ := ctx.ValueAs[library.Shelf](shelfContextKey)
    if shelf.Locked {
        errors.PermissionDenied.Detail(ctx, "shelf is locked",
            "unlock the shelf before modifying its books")
        return // the errors senders stop the request.
    }
    ctx.Next()
}
```

The contract is plain: call `ctx.Next()` to continue, or write a response
(the `rest/errors` senders stop the request for you) to end it. The full
controller, including the `requireShelf` guard that loads and caches the
shelf, is developed in [Chapter 4](https://iris-go.com/docs/routing-controllers/).

## Writing Custom Middleware

### A timing middleware

Go closures make middleware configuration natural. A closure is a
function value that keeps access to the variables of the scope where it
was created, so a returned handler can still read the arguments of the
function that built it, long after that function returned.

Configurable middleware is a factory function returning a handler; the
closure captures the configuration:

```go
package main

import (
    "time"

    "github.com/kataras/iris/v14"
)

// SlowRequestLogger warns about requests slower than "threshold".
func SlowRequestLogger(threshold time.Duration) iris.Handler {
    return func(ctx iris.Context) {
        start := time.Now()
        ctx.Next()

        if d := time.Since(start); d > threshold {
            ctx.Application().Logger().Warnf(
                "slow request: %s %s took %s",
                ctx.Method(), ctx.Path(), d)
        }
    }
}

func main() {
    app := iris.New()
    app.UseRouter(SlowRequestLogger(500 * time.Millisecond))

    app.Get("/", func(ctx iris.Context) {
        ctx.Text("ok")
    })

    app.Listen(":8080")
}
```

One caveat for post-processing: by the time `ctx.Next()` returns, the
response body has usually been written, and headers cannot change after
the first write. Code after `Next` should log, record metrics or update
state. Set response headers *before* calling `Next`.

### Request IDs with middleware/requestid

Correlating a response with its server-side logs is the classic middleware
job, and Iris ships it as `middleware/requestid`:

```go
package main

import (
    "github.com/kataras/iris/v14"
    "github.com/kataras/iris/v14/middleware/requestid"
)

func main() {
    app := iris.New()

    // Assign an ID to every request, even 404s.
    app.UseRouter(requestid.New())

    app.Get("/", func(ctx iris.Context) {
        ctx.Text("request id: " + requestid.Get(ctx))
    })

    app.Listen(":8080")
}
```

The default generator reuses a client-supplied `X-Request-Id` header or
generates a UUID, sets the response header and stores the ID on the
context (`ctx.ID()` returns it, `requestid.Get(ctx)` as a string).

Accepting the client's ID is convenient for tracing a call through
several services, but it also means the value lands in your logs exactly
as the client wrote it. Log injection is the abuse of that trust: input
crafted with newlines or control characters to forge or corrupt log
entries. When clients must not be able to choose or forge IDs, pass the
server-side generator:

```go
app.UseRouter(requestid.New(requestid.ServerOnlyGenerator))
```

## Conditional Middleware

`iris.NewConditionalHandler` combines a filter with one or more handlers
into a single registerable handler. The filter is an
`iris.Filter`, `func(iris.Context) bool`. When it returns true, the given
handlers are spliced into the current request's chain; when it returns
false, the chain simply continues without them. The wrapped handlers must
behave like middleware and call `ctx.Next()`.

```go
package main

import (
    "github.com/kataras/iris/v14"
)

func auditLog(ctx iris.Context) {
    ctx.Application().Logger().Infof("audit: %s %s",
        ctx.Method(), ctx.Path())
    ctx.Next()
}

func main() {
    app := iris.New()

    // Audit only mutating requests.
    app.Use(iris.NewConditionalHandler(func(ctx iris.Context) bool {
        return ctx.Method() != iris.MethodGet
    }, auditLog))

    app.Get("/items", func(ctx iris.Context) {
        ctx.Text("read")
    })
    app.Post("/items", func(ctx iris.Context) {
        ctx.Text("write")
    })

    app.Listen(":8080")
}
```

For coarser conditions you rarely need it: prefer attaching middleware to
the child router or subdomain whose requests it concerns, and let the
routing structure do the filtering.

## Error-Returning Middleware

Middleware that performs I/O (token validation, database lookups) reads
better when it can return an error instead of hand-writing a response.
`errors.FuncMiddleware` from `rest/errors` converts a
`func(ctx iris.Context) error` into a standard handler: a nil return calls
`ctx.Next()`, a non-nil return flows through the central error pipeline
(mapping, logging, JSON rendering) and stops the request.

```go
package main

import (
    "fmt"

    "github.com/kataras/iris/v14"
    "github.com/kataras/iris/v14/rest/errors"
)

// Accounts is the store the guard consults; in production it is a
// database.
type Accounts struct{ active map[string]bool }

func (a *Accounts) Active(ctx iris.Context, id string) (bool, error) {
    return a.active[id], nil
}

var accounts = &Accounts{active: map[string]bool{"acme": true}}

// requireActiveSubscription returns an error instead of writing a
// response; FuncMiddleware below turns it into an ordinary handler.
func requireActiveSubscription(ctx iris.Context) error {
    account := ctx.Params().GetString("account")

    active, err := accounts.Active(ctx, account)
    if err != nil {
        return err // the store is down: the central map makes it a 500.
    }

    if !active {
        return errors.FailedPrecondition.Wrap(
            fmt.Errorf("account %q has no active subscription", account))
    }

    return nil // continue the chain.
}

func main() {
    app := iris.New()

    data := app.Router("/{account:string}",
        errors.FuncMiddleware(requireActiveSubscription))
    data.Get("/data", func(ctx iris.Context) {
        ctx.JSON(iris.Map{"data": "value"})
    })

    app.Listen(":8080")
}
```

The check is a business rule, which is what this shape is for: it does
I/O, it can fail two ways, and only your code knows it. Authentication is
not in that class. A shared secret presented by a machine caller is
`apikey.New` from `middleware/apikey`, covered in
[Chapter 12](https://iris-go.com/docs/built-in-middleware/#api-keys), which reads the header,
compares in constant time, renders the 401 and names the caller for you.
Reading `X-API-Key` yourself in a middleware like the one above is the
version to stop writing.

`errors.FuncHandler` is the terminal-handler counterpart (no `Next` on
success), and `errors.NewRouter` wraps a whole router so every route and
middleware registration accepts error-returning functions; see
[Chapter 4](https://iris-go.com/docs/routing-controllers/) for the wrapper and
[Chapter 9](https://iris-go.com/docs/error-handling/) for the error map that gives returned
errors their status codes.

## What the Builder Already Registers

Applications assembled with `iris.NewBuilder` come with a middleware
baseline, installed once when the chain reaches
`RouterMiddlewares`/`Middlewares`. Do not register these again:

- The options handler for your `Errors(...)` options, via `UseRouter`,
  so every sender and `HandleError` call uses your map, logger and
  renderer.
- `errors.Handler`, via `UseRouter` after compression, and as the 404
  and 405 handler when the application is built. A bare failure status,
  an unknown path and (with `FireMethodNotAllowed`) a wrong method
  render as consistent JSON. To answer 404 or 405 yourself, register
  `HandleNoRoute`/`HandleNoMethod` from a `Configurator` passed to
  `NewBuilder`; the builder fills the two slots only when they are
  empty.
- Panic recovery: `recovery.WithErrorHandler(errors.Internal.LogErr)`
  followed by `recovery.Handler`, skipped if a handler named
  `iris.recovery` or `iris.errors.recover` is already present.
- The path-parameter type error handler: a malformed `{id:uuid}` renders
  400 `INVALID_ARGUMENT` instead of a bare 404.
- The context error handler for failed writes such as `ctx.JSON` errors.
- A `Server: Iris` response header, plus `X-Developer` when configured.
- `compress.Safe` when `Compression(true)` was selected, skipped if a
  handler named `iris.compression` exists.
- The rate-limit exceed handler, bound to the JSON error renderer.

Your own additions slot into the same two steps:
`RouterMiddlewares(...)` registers through `UseRouter` and
`Middlewares(...)` through `Use`, both accepting dependency-injected
factories as well as plain handlers; details in
[Chapter 3](https://iris-go.com/docs/iris-newbuilder/).

A panic is Go's runtime failure mode: a nil pointer dereference, an
out-of-range index or an explicit `panic()` call abruptly unwinds the
goroutine (the lightweight thread) serving the request. Left
unrecovered, the client gets a dropped connection with no response and
the failure surfaces only as a stack trace in the server log. Recovery
middleware wraps the chain in Go's `recover()` mechanism, so one broken
code path yields a logged error and a clean 500 instead.

With `iris.New()` none of this exists, and recovery is the one piece you
should always add yourself:

```go
package main

import (
    "github.com/kataras/iris/v14"
    "github.com/kataras/iris/v14/middleware/recovery"
)

func main() {
    app := iris.New()
    app.UseRouter(recovery.Handler)

    app.Get("/", func(ctx iris.Context) {
        panic("boom") // recovered: logged, 500 returned.
    })

    app.Listen(":8080")
}
```

To customize what a recovered panic returns, register a per-request error
handler before `recovery.Handler`:

```go
app.UseRouter(recovery.WithErrorHandler(
    func(ctx iris.Context, err error) {
        // Logs the panic and answers the canonical INTERNAL body; the
        // panic value itself never reaches the client.
        errors.Internal.LogErr(ctx, err)
    }), recovery.Handler)
```

## Ordering Rules

Three rules govern execution order, all verified by the router's own test
suite (`core/router/router_handlers_order_test.go`):

**1. Layers run outermost-first.** For a matched route:

\[Diagram: Router wrappers run first, then the one global UseRouter chain, then the mux, which either branches to the 404 and 405 chains or enters the matched route's frozen chain; code written after ctx.Next() runs on the way back out, innermost first.]

**2. Within a layer, registration order wins.** Two `Use` calls run in the
order they were made; handlers listed in one call run left to right. Code
placed *after* `ctx.Next()` runs in reverse order on the way out, like
deferred calls: the first middleware's post-processing executes last, so
a timing middleware registered first measures everything beneath it.

**3. A route's chain is assembled at registration time.** When `Get`,
`Post` or `Handle` runs, the router snapshots the `Use`/`UseRelevant`
handlers registered so far and bakes them into the route. Middleware
registered afterwards does not reach earlier routes, so register
middleware first, routes second. The builder's compiler-enforced step
order (`Middlewares` before `API`) exists precisely to make this mistake
impossible; with `iris.New()` the discipline is yours.

`UseRouter` and `WrapRouter` are the exception to rule 3: they belong to
the router itself rather than to individual routes, and take effect for
all requests once the application is built.

## Summary

- A middleware is an ordinary `iris.Handler` that calls `ctx.Next()` to
  continue or stops the chain: quietly by returning, or firmly with
  `ctx.StopExecution()` and the `StopWith*` shortcuts.
- Scope is chosen by registration method: `UseRouter` runs before routing
  for every request; root `Use` covers matched routes plus the 404/405
  chains; child `Use` (and `UseRelevant`) covers matched routes only;
  `UseOnce`/`UseRelevantOnce` insert-or-replace by handler name.
- Positional handlers guard single routes; guard methods on API structs
  share the controller's injected dependencies.
- `iris.NewConditionalHandler` gates middleware on a per-request filter;
  `errors.FuncMiddleware` lets middleware return errors into the central
  error pipeline.
- Builder-assembled applications already include recovery, error
  rendering, default headers and optional compression. Register only
  what you add on top. With `iris.New()`, start with
  `app.UseRouter(recovery.Handler)`.
- Layers run outermost-first, registration order wins within a layer, and
  route chains are frozen at route registration: middleware first,
  routes second.

## Further Reading

- [pkg.go.dev: net/http Handler](https://pkg.go.dev/net/http#Handler):
  the standard library interface `WrapRouter` exposes underneath Iris.
- [Go blog: Defer, Panic, and Recover](https://go.dev/blog/defer-panic-and-recover):
  the language mechanics behind recovery middleware.
- [A Tour of Go: Function closures](https://go.dev/tour/moretypes/25):
  the feature configurable middleware factories are built on.
- [MDN: Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS):
  why preflight requests exist and which headers answer them.
- [OWASP: Logging Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html):
  what to log, what never to log, and injection-safe log handling.
