nethttp

package
v6.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 18, 2024 License: Apache-2.0 Imports: 8 Imported by: 0

README

NetHTTP Middleware for Multitenancy

Go Reference

NetHTTP middleware provides multitenancy support for the net/http package in Go.

For valid tenant information, it calls the next handler. For missing or invalid tenant information, it sends "500 - Internal Server Error" response with the error message "Invalid tenant or tenant not found".

Usage

import (
    "net/http"

    nethttpmw "github.com/bartventer/gorm-multitenancy/v6/middleware/nethttp"
    "github.com/bartventer/gorm-multitenancy/v6/tenantcontext"
)

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        tenant := r.Context().Value(tenantcontext.TenantKey).(string)
        fmt.Fprintf(w, "Hello, %s", tenant)
    })

    handler := nethttpmw.WithTenant(nethttpmw.DefaultWithTenantConfig)(mux)

    http.ListenAndServe(":8080", handler)
}

Configuration

type WithTenantConfig struct {
	// Skipper defines a function to skip the middleware.
	Skipper func(r *http.Request) bool

	// TenantGetters is a list of functions that retrieve the tenant from the request.
	// Each function should return the tenant as a string and an error if any.
	// The functions are executed in order until a valid tenant is found.
	TenantGetters []func(r *http.Request) (string, error)

	// ContextKey is the key used to store the tenant in the context.
	ContextKey tenantcontext.ContextKey

	// ErrorHandler is a callback function that is called when an error occurs during the tenant retrieval process.
	ErrorHandler func(w http.ResponseWriter, r *http.Request, err error)

	// SuccessHandler is a callback function that is called after the tenant is successfully set in the http context.
	// It can be used to perform additional operations, such as modifying the database connection based on the tenant.
	SuccessHandler func(w http.ResponseWriter, r *http.Request)
}
Default Configuration
var DefaultWithTenantConfig = WithTenantConfig{
		Skipper: DefaultSkipper,
		TenantGetters: []func(r *http.Request) (string, error){
			DefaultTenantFromSubdomain,
			DefaultTenantFromHeader,
		},
		ContextKey: tenantcontext.TenantKey,
		ErrorHandler: func(w http.ResponseWriter, r *http.Request, _ error) {
			http.Error(w, ErrTenantInvalid.Error(), http.StatusInternalServerError)
		},
	}

Documentation

Overview

Package nethttp provides a middleware for the net/http package, which adds multi-tenancy support.

Example usage:

import (
    "net/http"

    nethttpmw "github.com/bartventer/gorm-multitenancy/v6/middleware/nethttp"
    "github.com/bartventer/gorm-multitenancy/v6/tenantcontext"
)

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        tenant := r.Context().Value(tenantcontext.TenantKey).(string)
        fmt.Fprintf(w, "Hello, %s", tenant)
    })

    handler := nethttpmw.WithTenant(nethttpmw.DefaultWithTenantConfig)(mux)

    http.ListenAndServe(":8080", handler)
}

Index

Examples

Constants

View Source
const (
	// XTenantHeader is the header key for the tenant.
	XTenantHeader = "X-Tenant"
)

Variables

View Source
var (
	// DefaultWithTenantConfig is the default configuration for the WithTenant middleware.
	// It uses the default skipper, tenant getters, context key, and error handler.
	DefaultWithTenantConfig = WithTenantConfig{
		Skipper: DefaultSkipper,
		TenantGetters: []func(r *http.Request) (string, error){
			DefaultTenantFromSubdomain,
			DefaultTenantFromHeader,
		},
		ContextKey: tenantcontext.TenantKey,
		ErrorHandler: func(w http.ResponseWriter, r *http.Request, _ error) {
			http.Error(w, ErrTenantInvalid.Error(), http.StatusInternalServerError)
		},
	}
)
View Source
var (
	// ErrTenantInvalid represents an error when the tenant is invalid or not found.
	ErrTenantInvalid = errors.New("invalid tenant or tenant not found")
)
View Source
var (
	// TenantKey is the key that holds the tenant in a request context.
	TenantKey = tenantcontext.TenantKey
)

Functions

func DefaultSkipper

func DefaultSkipper(r *http.Request) bool

DefaultSkipper represents the default skipper.

func DefaultTenantFromHeader

func DefaultTenantFromHeader(r *http.Request) (string, error)

DefaultTenantFromHeader extracts the tenant from the XTenantHeader header in the HTTP request. It returns the extracted tenant as a string and an error if the header is empty or missing.

Example
// Create a new HTTP request
req, err := http.NewRequest(http.MethodGet, "http://test.domain.com", nil)
if err != nil {
	fmt.Println("Error creating request:", err)
	return
}

// Set the XTenantHeader of the request
req.Header.Set(XTenantHeader, "test-tenant")

// Extract the tenant from the request
tenant, err := DefaultTenantFromHeader(req)
if err != nil {
	fmt.Println("Error:", err)
} else {
	fmt.Println("Tenant:", tenant)
}
Output:

Tenant: test-tenant

func DefaultTenantFromSubdomain

func DefaultTenantFromSubdomain(r *http.Request) (string, error)

DefaultTenantFromSubdomain extracts the subdomain from the given HTTP request's host. It removes the port from the host if present and adds a scheme to the host for parsing. The function then parses the URL and extracts the subdomain. It returns the extracted subdomain as a string and any error encountered during the process.

This function calls the ExtractSubdomain function to extract the subdomain from the host.

Example
// Create a new HTTP request
req, err := http.NewRequest(http.MethodGet, "http://test.domain.com", nil)
if err != nil {
	fmt.Println("Error creating request:", err)
	return
}

// Set the host of the request
req.Host = "test.domain.com"

// Extract the subdomain from the request
subdomain, err := DefaultTenantFromSubdomain(req)
if err != nil {
	fmt.Println("Error:", err)
} else {
	fmt.Println("Subdomain:", subdomain)
}
Output:

Subdomain: test

func ExtractSubdomain

func ExtractSubdomain(domainURL string) (string, error)

ExtractSubdomain extracts the first part of the subdomain from a given domain URL. If the URL has multiple subdomains, it only returns the first part.

Example
subdomain, err := ExtractSubdomain("http://test.domain.com")
if err != nil {
	fmt.Println("Error:", err)
} else {
	fmt.Println("Subdomain:", subdomain)
}

subdomain, err = ExtractSubdomain("http://test.sub.domain.com")
if err != nil {
	fmt.Println("Error:", err)
} else {
	fmt.Println("Subdomain:", subdomain)
}
Output:

Subdomain: test
Subdomain: test

func WithTenant

func WithTenant(config WithTenantConfig) func(http.Handler) http.Handler

WithTenant is a middleware function that adds multi-tenancy support to a net/http application. It takes a WithTenantConfig struct as input and returns a http.Handler. The WithTenantConfig struct allows customization of the middleware behavior. The middleware checks if the request should be skipped based on the Skipper function. It retrieves the tenant information using the TenantGetters functions. If an error occurs while retrieving the tenant, the ErrorHandler function is called. The retrieved tenant is then set in the request context using the ContextKey. Finally, the SuccessHandler function is called if provided, and the next handler is invoked.

Example
mux := http.NewServeMux()

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	tenant := r.Context().Value(tenantcontext.TenantKey).(string)
	fmt.Println("Tenant:", tenant)
})

handler := WithTenant(DefaultWithTenantConfig)(mux)

req := httptest.NewRequest(http.MethodGet, "/", nil)
req.Host = "tenant.example.com"
rec := httptest.NewRecorder()

// Execute the request
handler.ServeHTTP(rec, req)
Output:

Tenant: tenant

Types

type WithTenantConfig

type WithTenantConfig struct {
	// Skipper defines a function to skip the middleware.
	Skipper func(r *http.Request) bool

	// TenantGetters is a list of functions that retrieve the tenant from the request.
	// Each function should return the tenant as a string and an error if any.
	// The functions are executed in order until a valid tenant is found.
	TenantGetters []func(r *http.Request) (string, error)

	// ContextKey is the key used to store the tenant in the context.
	ContextKey tenantcontext.ContextKey

	// ErrorHandler is a callback function that is called when an error occurs during the tenant retrieval process.
	ErrorHandler func(w http.ResponseWriter, r *http.Request, err error)

	// SuccessHandler is a callback function that is called after the tenant is successfully set in the http context.
	// It can be used to perform additional operations, such as modifying the database connection based on the tenant.
	SuccessHandler func(w http.ResponseWriter, r *http.Request)
}

WithTenantConfig represents the configuration options for the tenant middleware in net/http.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL