In this guide, we’ll walk through how to create CORS (Cross-Origin Resource Sharing) and a health check endpoint in a Golang application using the Chi router.

Setting Up Chi and CORS Middleware

We’ll use the go-chi/chi and go-chi/cors packages to handle routing and CORS in our application.

Step 1: Import Required Packages

First, make sure to import the necessary packages:

import (
	"fmt"
	"net/http"
	"log"

	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/go-chi/cors"
	AdminMiddleware "github.com/project/api/middleware"
	"github.com/project/api/services"
	"gorm.io/gorm"
)

Step 2: Define the CORS AllowOriginFunc

We’ll define a function to specify the allowed origins for CORS. This function also logs disallowed origins:

func AllowOriginFunc(r *http.Request, origin string) bool {
	allowedOrigins := []string{"http://localhost", "https://admin-micro-qa.clorian.com"}
	for _, o := range allowedOrigins {
		if origin == o {
			return true
		}
	}

	// Get the client's IP address
	clientIP := r.RemoteAddr

	// Print the log in red color
	red := "\033[31m"
	reset := "\033[0m"
	log.Printf("%sDisallowed origin: %s, Client IP: %s%s", red, origin, clientIP, reset)

	return false
}

In this function:

  • allowedOrigins: This slice contains the origins that are permitted to access the resources.
  • Loop through allowed origins: The function checks if the request origin is in the list of allowed origins.
  • Logging disallowed origins: If the origin is not allowed, the function logs this event using log.Printf.
Explanation of log.Printf

log.Printf is a function from the Go log package that formats according to a format specifier and writes to the standard logger.

In this code:

  • red and reset are ANSI escape codes for coloring the output in the terminal. \033[31m sets the text color to red, and \033[0m resets it to the default color.
  • log.Printf prints a formatted string that includes the disallowed origin and the client’s IP address in red color to emphasize that it is a disallowed request. The formatted string includes placeholders (%s) which are replaced by the specified variables (origin and clientIP).

Step 3: Initialize the Router

Now we initialize the router, configure middleware, and set up CORS:

func Init(db *gorm.DB) *chi.Mux {
	r := chi.NewRouter()
	r.Use(middleware.Logger)
	sm := services.NewServiceManager(db)
	r.Use(cors.Handler(cors.Options{
		AllowOriginFunc:  AllowOriginFunc,
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders:   []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
		ExposedHeaders:   []string{"Link"},
		AllowCredentials: true,
		MaxAge:           300,
	}))

	r.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello, Go!")
	})

	return r
}

Setting Up the Health Check Endpoint

The health check endpoint is a simple route that responds with a message indicating the service is running:

r.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello, Go!")
})

Putting It All Together

Here’s the complete code:

import (
	"fmt"
	"net/http"
	"log"

	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/go-chi/cors"
	AdminMiddleware "github.com/project/api/middleware"
	"github.com/project/api/services"
	"gorm.io/gorm"
)

func AllowOriginFunc(r *http.Request, origin string) bool {
	allowedOrigins := []string{"http://localhost", "https://admin-micro-qa.clorian.com"}
	for _, o := range allowedOrigins {
		if origin == o {
			return true
		}
	}

	clientIP := r.RemoteAddr

	red := "\033[31m"
	reset := "\033[0m"
	log.Printf("%sDisallowed origin: %s, Client IP: %s%s", red, origin, clientIP, reset)

	return false
}

func Init(db *gorm.DB) *chi.Mux {
	r := chi.NewRouter()
	r.Use(middleware.Logger)
	sm := services.NewServiceManager(db)
	r.Use(cors.Handler(cors.Options{
		AllowOriginFunc:  AllowOriginFunc,
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders:   []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
		ExposedHeaders:   []string{"Link"},
		AllowCredentials: true,
		MaxAge:           300,
	}))

	r.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello, Go!")
	})

	return r
}

This setup ensures your Go application can handle cross-origin requests securely and provides a health check endpoint to monitor the application’s status.