# Middleware

Pada bab ini kita akan membuat middleware. Kasus yang digunakan adalah handling auth.

* Buat file baru libraries/api/middleware.go

```go
package api

// Middleware is a function designed to run some code before and/or after
// another Handler. It is designed to remove boilerplate or other concerns not
// direct to any given Handler.
type Middleware func(Handler) Handler

// wrapMiddleware creates a new handler by wrapping middleware around a final
// handler. The middlewares' Handlers will be executed by requests in the order
// they are provided.
func wrapMiddleware(mw []Middleware, handler Handler) Handler {

    // Loop backwards through the middleware invoking each one. Replace the
    // handler with the new wrapped handler. Looping backwards ensures that the
    // first middleware of the slice is the first to be executed by requests.
    for i := len(mw) - 1; i >= 0; i-- {
        h := mw[i]
        if h != nil {
            handler = h(handler)
        }
    }

    return handler
}
```

* list semua middleware yang diperlukan pada routing/route.go

```go
package routing

import (
    "database/sql"
    "essentials/controllers"
    "essentials/libraries/api"
    "essentials/middlewares"
    "log"
    "net/http"
)

func mid(db *sql.DB, log *log.Logger) []api.Middleware {
    var mw []api.Middleware
    mw = append(mw, middlewares.Auths(db, log, []string{"/login"}))

    return mw
}

// API handling routing
func API(db *sql.DB, log *log.Logger) http.Handler {
    app := api.NewApp(log, mid(db, log)...)
    app.HandleCors()

    // Users routing
    {
        users := controllers.Users{Db: db, Log: log}
        app.Handle(http.MethodGet, "/users", users.List)
        app.Handle(http.MethodPost, "/users", users.Create)
        app.Handle(http.MethodGet, "/users/:id", users.View)
        app.Handle(http.MethodPut, "/users/:id", users.Update)
        app.Handle(http.MethodDelete, "/users/:id", users.Delete)
    }

    return app
}
```

* Tambahkan field middleware di type App libraries/api/app.go

```go
package api

import (
    "context"
    "log"
    "net/http"
    "time"

    "github.com/julienschmidt/httprouter"
)

// App struct for new api
type App struct {
    log *log.Logger
    mux *httprouter.Router
    mw  []Middleware
}

// Handler type as standard http.Handle
type Handler func(http.ResponseWriter, *http.Request)

// Ctx type for encapsulated context key
type Ctx string

// Handle associates a httprouter Handle function with an HTTP Method and URL pattern.
func (a *App) Handle(method, url string, h Handler) {
    h = wrapMiddleware(a.mw, h)

    fn := func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
        ctx := context.WithValue(r.Context(), Ctx("ps"), ps)
        //const timeout = 1 * time.Second
        //ctx2, cancel := context.WithTimeout(ctx, timeout)
        //defer cancel()

        header := w.Header()
        header.Add("Access-Control-Allow-Origin", "*")
        header.Add("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS, PUT")
        header.Add("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Token")
        header.Add("Content-Type", "application/json; charset=utf-8")

        h(w, r.WithContext(ctx))
    }

    a.mux.Handle(method, url, fn)
}

// HandleCors and OPTIONS response
func (a *App) HandleCors() {
    a.mux.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Header.Get("Access-Control-Request-Method") != "" {
            // Set CORS headers
            header := w.Header()
            header.Add("Access-Control-Allow-Origin", "*")
            header.Add("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS, PUT")
            header.Add("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Token")
            header.Add("Content-Type", "application/json; charset=utf-8")
        }

        // Adjust status code to 204
        w.WriteHeader(http.StatusNoContent)
    })
}

// ServeHTTP implements the http.Handler interface
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    a.mux.ServeHTTP(w, r)
}

// NewApp for create new api
func NewApp(log *log.Logger, mw ...Middleware) *App {
    return &App{
        log: log,
        mux: httprouter.New(),
        mw:  mw,
    }
}
```

* Buat middleware untuk handling authorization ( middlewares/auth.go )

```go
package middlewares

import (
    "database/sql"
    "errors"
    "log"
    "net/http"

    "essentials/libraries/api"
)

// Auths middleware
func Auths(db *sql.DB, log *log.Logger, allow []string) api.Middleware {
    fn := func(before api.Handler) api.Handler {
        h := func(w http.ResponseWriter, r *http.Request) {
            var isAuth bool

            // hardcode athorization for true.
            // upcoming chapter, this line will execute RBAC checking
            isAuth = true

            if !isAuth {
                api.ResponseError(w, api.ErrForbidden(errors.New("Forbidden"), ""))
            } else {
                before(w, r)
            }
        }

        return h
    }

    return fn
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://golang-microservices.rijalasepnugroho.com/build-rest-api-framework/middleware.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
