web

package module
v0.0.0-...-6c1b057 Latest Latest
Warning

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

Go to latest
Published: Mar 21, 2023 License: Apache-2.0 Imports: 18 Imported by: 0

README

web Go Report Card Go Reference GitHub license

Web is a minimalist router for Go to create web applications (server-side) without third-party dependencies. Web will always be compatible with the standard Go library; HTTP handlers have the same signature as http.HandlerFunc.

Router

Web has a simplified, linear path-matching router and supports URI definition with the following patterns:

  1. /api/users - URI with no dynamic values
  2. /api/users/:userID
    • URI with a named parameter, userID
    • If TrailingSlash is true, a URI ending in '/' will be accepted, see sample.
  3. /api/users/:misc*
    • Named URI parameter misc, with a wildcard suffix '*'
    • This matches everything after /api/users. e.g. /api/users/a/b/c/d

If there are multiple handlers corresponding to the same URI, the request will only be handled by the first encountered handler. Refer to sample to see how routes are configured. You can access the named URI parameters with the Context function.

Note: Web Context not available inside special handlers.

func helloWorld(w http.ResponseWriter, r *http.Request) {
	wctx := web.Context(r)
	// URI paramaters, map[string]string
	params := wctx.Params()
	// route, the web.Route which is executing this request
	route := wctx.Route
	web.R200(
		w,
		fmt.Sprintf(
			"Route name: '%s', params: '%s'",
			route.Name,
			params,
		),
	)
}

Handler chaining

Handler chaining allows to execute multiple handlers for a given route. Chaining execution can be set to run even after the handler has written a response to an HTTP request by setting FallThroughPostResponse to true (see sample).

Middleware

Web middlware allows you to wrap all routes with middleware as opposed to a handler chain. The router exposes the Use and UseOnSpecialHandlers methods to add Middleware to the router.

NotFound and NotImplemented are considered special handlers. The web.Context(r) inside special handlers will return nil.

You can add any number of intermediate programs to the router, the execution order of the intermediate programs will be LIFO (Last In First Out). E.g.:

func main() {
	router.Use(accesslog.AccessLog, cors.CORS(nil))
	router.Use(<more middleware>)
}

First CorsWrap will be executed, then AccessLog.

Error handling

Web context has 2 methods for set and get errors in the request context. This allows the Web to implement a single middleware where errors returned in the HTTP handler can be handled. set error, get error.

Helper functions

Web provides several helper functions. When using Send or SendResponse the response is wrapped in response struct Web and serialized as JSON.

{
  "data": "<any valid JSON payload>",
  "status": "<HTTP status code, of type integer>"
}

Using SendError, the response is wrapped in error response struct Web and serialized as JSON.

{
  "errors": "<any valid JSON payload>",
  "status": "<HTTP status code, of type integer>"
}

HTTPS ready

The HTTPS server can be easily started by providing a key and a cert file. You can also have both HTTP and HTTPS servers running side by side.

Start HTTPS server

cfg := &web.Config{
	Port: "80",
	HTTPSPort: "443",
	CertFile: "/path/to/certfile",
	KeyFile: "/path/to/keyfile",
}
router := web.NewRouter(cfg, routes()...)
router.StartHTTPS()

Starting both HTTP & HTTPS server

cfg := &web.Config{
	Port: "80",
	HTTPSPort: "443",
	CertFile: "/path/to/certfile",
	KeyFile: "/path/to/keyfile",
}

router := web.NewRouter(cfg, routes()...)
go router.StartHTTPS()
router.Start()

Graceful shutdown

Graceful shutdown allows you to shut down the server without affecting live connections/clients connected to the server. Any new connection request after initiating the shutdown will be ignored.
Sample:

func main() {
	osSig := make(chan os.Signal, 5)

	cfg := &web.Config{
		Host:            "",
		Port:            "8080",
		ReadTimeout:     15 * time.Second,
		WriteTimeout:    60 * time.Second,
		ShutdownTimeout: 15 * time.Second,
	}
	router := web.NewRouter(cfg, routes()...)

	go func() {
		<-osSig
		// Initiate HTTP server shutdown
		err := router.Shutdown()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		} else {
			fmt.Println("shutdown complete")
			os.Exit(0)
		}

		// If HTTPS server running
		err := router.ShutdownHTTPS()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		} else {
			fmt.Println("shutdown complete")
			os.Exit(0)
		}
	}()

	go func(){
		time.Sleep(time.Second*15)
		signal.Notify(osSig, os.Interrupt, syscall.SIGTERM)
	}()

	router.Start()
}

Logging

Web exposes a singleton & global scoped logger variable LOGHANDLER with which you can plug in your custom logger by implementing the Logger interface.

Configuring the default Logger

The default logger uses the standard Go log.Logger library with os.Stdout for debugging and information logs and os.Stderr for warnings, errors, fatal events as io.Writers by default. You can set io.Writer and also disable certain log types with GlobalLoggerConfig(stdout, stderr, cfgs...).

Server-Sent Events

MDN has very good documentation on what SSE (Server-Sent Events) is.

Usage

A fully functional sample is available here.

Documentation

Overview

The web package is a lightweight framework for building web applications. It has a multiplexer, a mechanism for connecting middleware, and its own context management. The main goal of web is to get as far away from the developer as possible, i.e. it doesn't force you to build your application according to any particular template, but just helps you make all the trivial things faster and easier. e.g. 1. Getting named URI parameters. 2. Multiplexer for regex-matching URIs and the like. 3. Implementation of special application-level configurations or any similar objects into the request context as required.

Index

Constants

View Source
const (
	// LogCfgDisableDebug is used to disable debug logs
	LogCfgDisableDebug = logCfg("disable-debug")
	// LogCfgDisableInfo is used to disable info logs
	LogCfgDisableInfo = logCfg("disable-info")
	// LogCfgDisableWarn is used to disable warning logs
	LogCfgDisableWarn = logCfg("disable-warn")
	// LogCfgDisableError is used to disable error logs
	LogCfgDisableError = logCfg("disable-err")
	// LogCfgDisableFatal is used to disable fatal logs
	LogCfgDisableFatal = logCfg("disable-fatal")
)
View Source
const (
	// HeaderContentType is a key to refer to the content type of the response header
	HeaderContentType = "Content-Type"
	// JSONContentType is the MIME type when the response is JSON
	JSONContentType = "application/json"
	// HTMLContentType is the MIME type when the response is HTML
	HTMLContentType = "text/html; charset=UTF-8"
	// ErrInternalServer to send when an internal server error
	ErrInternalServer = "Internal server error"
)

Variables

This section is empty.

Functions

func GetError

func GetError(r *http.Request) error

GetError is an auxiliary function to get the error from the web context

func GlobalLoggerConfig

func GlobalLoggerConfig(stdout io.Writer, stderr io.Writer, cfgs ...logCfg)

GlobalLoggerConfig is used to configure the global/default web logger. IMPORTANT: It is not safe for simultaneous operation.

func OriginalResponseWriter

func OriginalResponseWriter(rw http.ResponseWriter) http.ResponseWriter

OriginalResponseWriter returns the Go response record stored in the custom web response record.

func R200

func R200(w http.ResponseWriter, data interface{})

R200 - Successful/OK response

func R201

func R201(w http.ResponseWriter, data interface{})

R201 - New item created

func R204

func R204(w http.ResponseWriter)

R204 - empty, no content

func R302

func R302(w http.ResponseWriter, data interface{})

R302 - Temporary redirect

func R400

func R400(w http.ResponseWriter, data interface{})

R400 - Invalid request, any incorrect/erraneous value in the request body

func R403

func R403(w http.ResponseWriter, data interface{})

R403 - Unauthorized access

func R404

func R404(w http.ResponseWriter, data interface{})

R404 - Resource not found

func R406

func R406(w http.ResponseWriter, data interface{})

R406 - Unacceptable header. For any error related to values set in header

func R451

func R451(w http.ResponseWriter, data interface{})

R451 - Resource taken down because of a legal request

func R500

func R500(w http.ResponseWriter, data interface{})

R500 - Internal server error

func Render

func Render(w http.ResponseWriter, data interface{}, rCode int, tpl *template.Template)

Render is used for rendering templates (HTML)

func ResponseStatus

func ResponseStatus(rw http.ResponseWriter) int

ResponseStatus returns the response status code. This only works if http.ResponseWriter is not wrapped in another response writer before calling ResponseStatus.

func Send

func Send(w http.ResponseWriter, contentType string, data interface{}, rCode int)

Send sends a completely custom response without wrapping it in `{data: <data>, status: <int>` struct

func SendError

func SendError(w http.ResponseWriter, data interface{}, rCode int)

SendError is used to respond to any request with an error

func SendHeader

func SendHeader(w http.ResponseWriter, rCode int)

SendHeader is used to send only a response header, i.e no response body

func SendResponse

func SendResponse(w http.ResponseWriter, data interface{}, rCode int)

SendResponse is used to respond to any request (JSON response) based on the code, data etc.

func SetError

func SetError(r *http.Request, err error)

SetError is an auxiliary function for setting an error in the web context

Types

type Config

type Config struct {
	// Host is the host on which the server is listening
	Host string `json:"host,omitempty"`
	// Port is the port number on which the server should listen to HTTP requests
	Port string `json:"port,omitempty"`

	// CertFile is the path to TLS/SSL certificate file required for HTTPS
	CertFile string `json:"certFile,omitempty"`
	// KeyFile is the path to the certificate private key file
	KeyFile string `json:"keyFile,omitempty"`
	// HTTPSPort is the port number on which the server should listen to HTTP requests
	HTTPSPort string `json:"httpsPort,omitempty"`

	// ReadTimeout is the maximum length of time for which the server will read the request
	ReadTimeout time.Duration `json:"readTimeout,omitempty"`
	// WriteTimeout is the maximum time for which the server will try to respond to the request
	WriteTimeout time.Duration `json:"writeTimeout,omitempty"`

	// InsecureSkipVerify is the HTTP certificate verification
	InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`

	// ShutdownTimeout is the duration during which the preferential shutdown will be completed
	ShutdownTimeout time.Duration

	// ReverseMiddleware, if true,
	// will change the execution order of the middleware from the order it was added.
	// e.g. router.Use(m1,m2), m2 will be executed first if ReverseMiddleware is true
	ReverseMiddleware bool
}

Config is used to read the application configuration from a json file

func (*Config) Load

func (cfg *Config) Load(filepath string)

Loads config file from the provided filepath and validate

func (*Config) Validate

func (cfg *Config) Validate() error

Validate the config parsed into the Config struct

type ContextPayload

type ContextPayload struct {
	Route     *Route
	Err       error
	URIParams map[string]string
}

ContextPayload is a WebContext. A new ContextPayload instance is injected inside the context object of each request.

func Context

func Context(r *http.Request) *ContextPayload

Context returns the ContextPayload injected inside the HTTP request context.

func (*ContextPayload) Error

func (cp *ContextPayload) Error() error

Error returns the error set within the context.

func (*ContextPayload) Params

func (cp *ContextPayload) Params() map[string]string

Params returns the URI parameters of the corresponding route.

func (*ContextPayload) SetError

func (cp *ContextPayload) SetError(err error)

SetError sets the value of err in context.

type ErrorData

type ErrorData struct {
	ErrCode        int
	ErrDescription string
}

ErrorData used to render the error page

type Logger

type Logger interface {
	Debug(data ...interface{})
	Info(data ...interface{})
	Warn(data ...interface{})
	Error(data ...interface{})
	Fatal(data ...interface{})
}

Logger defines all the logging methods to be implemented

var (
	// ErrInvalidPort is the error returned when the port number provided in the config file is invalid
	ErrInvalidPort = errors.New("Port number not provided or is invalid (should be between 0 - 65535)")

	// LOGHANDLER is a global variable which web uses to log messages
	LOGHANDLER Logger
)

type Middleware

type Middleware func(http.ResponseWriter, *http.Request, http.HandlerFunc)

Middleware is the signature of Web's middleware

type Route

type Route struct {
	// Name is unique identifier for the route
	Name string
	// Method is the HTTP request method/type
	Method string
	// Pattern is the URI pattern to match
	Pattern string
	// TrailingSlash if set to true, the URI will be matched with or without
	// a trailing slash. IMPORTANT: It does not redirect.
	TrailingSlash bool

	// FallThroughPostResponse if enabled will execute all the handlers even if a response was already sent to the client
	FallThroughPostResponse bool

	// Handlers is a slice of http.HandlerFunc which can be middlewares or anything else. Though only 1 of them will be allowed to respond to client.
	// subsequent writes from the following handlers will be ignored
	Handlers []http.HandlerFunc
	// contains filtered or unexported fields
}

Route defines a route for each API

type RouteGroup

type RouteGroup struct {

	// PathPrefix is the URI prefix for all routes in this group
	PathPrefix string
	// contains filtered or unexported fields
}

func NewRouteGroup

func NewRouteGroup(pathPrefix string, skipRouterMiddleware bool, rr ...Route) *RouteGroup

func (*RouteGroup) Add

func (rg *RouteGroup) Add(rr ...Route)

func (*RouteGroup) Routes

func (rg *RouteGroup) Routes() []*Route

func (*RouteGroup) Use

func (rg *RouteGroup) Use(mm ...Middleware)

type Router

type Router struct {

	// NotFound is the generic handler for 404 resource not found response
	NotFound http.HandlerFunc

	// NotImplemented is the generic handler for 501 method not implemented
	NotImplemented http.HandlerFunc
	// contains filtered or unexported fields
}

Router is the HTTP router

func NewRouter

func NewRouter(cfg *Config, routes ...*Route) *Router

NewRouter initializes & returns a new router instance with all the configurations and routes set

func (*Router) Add

func (rtr *Router) Add(routes ...*Route)

Add is a convenience method used to add a new route to an already initialized router Important: `.Use` should be used only after all routes are added

func (*Router) ServeHTTP

func (rtr *Router) ServeHTTP(rw http.ResponseWriter, r *http.Request)

func (*Router) SetupMiddleware

func (router *Router) SetupMiddleware()

SetupMiddleware initializes all middleware added with "Use". This function does not need to be called explicitly if router.Start() or router.StartHTTPS() is used. Instead, if the router is passed to an external server, the SetupMiddleware function should be called.

func (*Router) Shutdown

func (router *Router) Shutdown() error

Shutdown gracefully shuts down HTTP server

func (*Router) ShutdownHTTPS

func (router *Router) ShutdownHTTPS() error

ShutdownHTTPS gracefully shuts down HTTPS server

func (*Router) Start

func (router *Router) Start()

Start starts the HTTP server with the appropriate configurations

func (*Router) StartHTTPS

func (router *Router) StartHTTPS()

StartHTTPS starts the server with HTTPS enabled

func (*Router) Use

func (rtr *Router) Use(mm ...Middleware)

Use adds a middleware layer

func (*Router) UseOnSpecialHandlers

func (rtr *Router) UseOnSpecialHandlers(mm ...Middleware)

UseOnSpecialHandlers adds middleware to 2 special web handlers

Directories

Path Synopsis
extensions
sse
The sse package implements Server-Sent Events (SSE).
The sse package implements Server-Sent Events (SSE).
middleware
accesslog
The accesslogs package provides a simple middleware for access logs.
The accesslogs package provides a simple middleware for access logs.
cors
The cors package sets the appropriate CORS (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) response headers and allows for customization.
The cors package sets the appropriate CORS (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) response headers and allows for customization.

Jump to

Keyboard shortcuts

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