Go fiber custom header middleware

go fiber custom header filter middleware

Hi guys, today we are back again with an another go fiber middleware tutorial. In this tutorial we will be building a custom header middleware for our go fiber app. In one of our previous tutorial we learn how we can build a custom go fiber middleware and if you haven’t checked that out than I would definitely recommend you to do so as here as we will be reusing most of the things from that article. So without a further due let’s get started.

Agenda

Recently I was designing a custom proxy solution (primarily for web scraping) with go fiber as my front-end server where I decided to allow users to pass on headers that they want to pass on to their target website. For example they may want to pass in their own user-agent or something along those. Moreover, I also want the custom headers passed along with the regular headers itself without needing the user to pass them in a separate body or in url’s query. So how does that work? Let see.

go fiber custom header middleware working
Overview of our middleware

I needed a way to distinguish the “user’s headers” from regular headers. So I thought why not only consider headers with a prefix “X-Custom-” as valid user’s headers and only pass on those to the user’s target website. It’s pretty much straight forward from here on as all we need to do is filter out all headers with prefix “X-Custom-“.

Please do note that when I say regular headers, I mean all headers that aren’t prefixed with “X-Custom-“.

Initialize a go fiber app

Initializing a go fiber app is straight forward as typing in the below commands

go mod init my-project
go get -u github.com/gofiber/fiber/v2

Project directory structure

go-middleware-tutorial/
┣ custom-header/
┃ ┗ custom-header.go
┣ go.mod
┣ go.sum
┗ main.go

Under our root directory, we create another directory /custom-header inside which our custom middleware file custom-header.go will go in. Also main.go is our app entry file whose content is as below.

package main

import (
     // import our middleware(which we will create shortly)
     "go-middleware/customheader"
     "log"

     "github.com/gofiber/fiber/v2"

)

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

    // here we will use our middleware
    app.Use(/*here we use our custom header middleware*/)

    // test route
    app.Get("/test", func(c *fiber.Ctx) error {
	
    })

    log.Fatal(app.Listen(":3000"))
}

Create our custom header middleware

Below will be the structure of our go fiber custom header middleware. Basically it has the same structure as our go fiber custom middleware tutorial.

package customheader

import (
	"strings"

	"github.com/gofiber/fiber/v2"
)

// utility function
func InArray(val string, list []string) bool {
	for _, item := range list {
		if val == item {
			return true
		}
	}
	return false
}

/* 
	REQUIRED(Any middleware must have this)

	For every middleware we need a config.
	In config we also need to define a function which allows us to skip the middleware if return true.
	By convention it should be named as "Filter" but any other name will work too.
*/

type Config struct {
	Filter     func(c *fiber.Ctx) bool
	StartsWith string
	Exclude    []string
}

var ConfigDefault = Config{
	Filter: func(c *fiber.Ctx) bool {
		// returning true skips the middleware
		return false

	},
	StartsWith: "X-Custom-",
	Exclude:    []string{},
}

/*
	Middleware specific

	Function for generating default config
*/
func configDefault(config ...Config) Config {
}

/*
    REQUIRED(Any middleware must have this)

    Our main middleware function used to initialize our middleware.
    By convention we name it "New" but any other name will work too.
*/
func New(config Config) fiber.Handler {
}

Config

Our config is a basic one with the below fields.

type Config struct {
	Filter     func(c *fiber.Ctx) bool
        
        // we can target any header that starts with StartWith
	StartsWith string
        
        /* if we want to exclude any header we put in here, eg. if we want
        excluded say header say X-Custom-myheader, we put X-Custom-myheader
        in Exclude array
        */
	Exclude    []string
}

configDefault

This is a utility function that returns default values for our config in case we haven’t provided one ourselves.

func configDefault(config ...Config) Config {

	if len(config) < 1 {
		return ConfigDefault
	}

	// Override default config
	cfg := config[0]

	// Set default values if not passed
	if cfg.Filter == nil {
		cfg.Filter = ConfigDefault.Filter
	}

	if cfg.StartsWith == "" {
		cfg.StartsWith = ConfigDefault.StartsWith
	}

	if len(cfg.Exclude) <= 0 {
		cfg.Exclude = ConfigDefault.Exclude
	}

	return cfg
}

New function to instantiate our custom header middleware

The New function expects a Config as a parameter and returns a fiber.handler. At the very top we have ensuring we have set up proper config. Next we iterate thought each of our headers and filter out those that starts with our “StartWith” string passed in the config. In our case it’s “X-Custom-“.

We also check make sure we don’t include any header if we have that in our Exclude array.

// For setting default config
	cfg := configDefault(config)

	return func(c *fiber.Ctx) error {
		// Don't execute middleware if Filter returns true
		if cfg.Filter != nil && cfg.Filter(c) {
			return c.Next()
		}

		headers := c.GetReqHeaders()

		custHeaders := make(map[string]interface{})

		for key, val := range headers {
			if !InArray(key, cfg.Exclude) && strings.HasPrefix(key, cfg.StartsWith) {
				key = strings.TrimSpace(key)
				if key == "" {
					continue
				}
				custHeaders[key[len(cfg.StartsWith):]] = strings.TrimSpace(val)
			}
		}

                // we store it Locals so that it available down the route
		c.Locals(cfg.StartsWith+"Headers", custHeaders)

		return c.Next()
	}

With that we are done with our middleware and can move on to test it in our app.

Using our middleware

app.Use(customheader.New(customheader.Config{}))

Or we can also pass in a custom StartWith string or Exclude array.

app.Use(customheader.New(customheader.Config{
   StartsWith: 'Y-Myheader',
   Exclude: []string{
     'Y-Myheader-User-Agent',
   }
}))

Because we have passed in Y-Myheader-User-Agent in Exclude, it won’t be included in c.Locals(“Y-Myheader-Headers”). Apart from that we can also provide in very own Filter method in our config if we want to. Please do not confuse Filter method with filtering headers. It’s just a method you can use to skip the middleware based on your own custom logic. Here we won’t provide in a custom Filter method as we don’t want to skip our middleware.

Testing our custom header middleware

To test our middleware we we just create a test route /test.

app.Get("/test", func(c *fiber.Ctx) error {
    /* 
      if you remember we store our headers in Local with a key X-Custom-Headers
      so if your StartWith = Y-Myheader, then the key would be Y-Myheader-Headers
    */
    fmt.Println(c.Locals("X-Custom-Headers"))
})

Conclusion

Finally, we have reached the end of our second tutorial on go fiber middleware, and hope you guys liked it. In case you have any doubts or questions please feel free to leave a comment down below otherwise show us your love by sharing it with your friends and on social media.

Leave a Reply

Back To Top