webgo

package module
v4.1.11 Latest Latest
Warning

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

Go to latest
Published: Feb 13, 2021 License: MIT Imports: 18 Imported by: 2

README

webgo gopher

coverage

WebGo v4.1.11

WebGo is a minimalistic framework for Go to build web applications (server side) with zero 3rd party dependencies. WebGo will always be Go standard library compliant; with the HTTP handlers having the same signature as http.HandlerFunc.

Index
  1. Router
  2. Handler chaining
  3. Middleware
  4. Helper functions
  5. HTTPS ready
  6. Graceful shutdown
  7. Logging
  8. Usage

Router

Router routes multiple paths/URIs to its respective HTTP handler. It supports defining URIs with the following patterns

  1. /api/users
    • Static URI pattern with no variables
  2. /api/users/:userID
    • URI pattern with named variable userID (named URI parameter)
    • This will not match /api/users/johndoe/account. It only matches till /api/users/johndoe/
      • If TrailingSlash is set to true, refer to sample
  3. /api/users/:misc*
    • Named URI variable misc
    • This matches everything after /api/users. e.g. /api/users/a/b/c/d

If multiple patterns match the same URI, the first matching handler would be executed. Refer to the sample to see how routes are configured. A WebGo Route is defined as following:

webgo.Route{
	// A name for the API (preferrably unique)
	Name string
	// HTTP verb, i.e. GET, POST, PUT, PATCH, HEAD, DELETE
	Method string
	// The URI pattern
	Pattern string
	// If the URI ends with a '/', should it be considered valid or not? e.g. '/api/users' vs '/api/users/'
	TrailingSlash bool
	// In case of chained handlers, should the execution continue after one of the handlers have 
	// responded to the HTTP request
	FallThroughPostResponse bool
	// The list of HTTP handlers
	Handlers []http.HandlerFunc
}

You can access named parameters of the URI using the Context function. Note: webgo Context is not available inside special handlers, since it serves no purpose

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

Handler chaining

Handler chaining lets you execute multiple handlers for a given route. Execution of a chain can be configured to run even after a handler has written a response to the HTTP request. This is made possible by setting FallThroughPostResponse to true (refer sample).

webgo.Route{
	Name: "chained",
	Method: http.MethodGet,
	Pattern: "/api",
	TrailingSlash: false,
	FallThroughPostResponse: true,
	Handlers []http.HandlerFunc{
		handler1,
		handler2,
		.
		.
		.
	}
}

Middleware

WebGo middlware lets you wrap all the routes with a middleware unlike handler chaining. The router exposes a method Use && UseOnSpecialHandlers to add a Middleware to the router. Following code shows how a middleware can be used.

NotFound && NotImplemented are the handlers which are considered Special handlers. webgo.Context(r) within special handlers will return nil.

import (
	"github.com/bnkamalesh/webgo/v4"
	"github.com/bnkamalesh/webgo/v4/middleware"
)

func routes() []*webgo.Route {
	return []*webgo.Route{
		&webo.Route{
			Name: "home",
			Method: http.http.MethodGet,
			Pattern: "/",
			Handlers: []http.HandlerFunc{
				func(w http.ResponseWriter, r *http.Request) {
					webgo.R200(w, "home")
				}
			},
		},
	}
}

func main() {
	router := webgo.NewRouter(*webgo.Config{
		Host:         "",
		Port:         "8080",
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 60 * time.Second,
	}, routes())

	router.UseOnSpecialHandlers(middleware.AccessLog)
	
	router.Use(middleware.AccessLog)

	router.Start()
}

Any number of middleware can be added to the router, the order of execution of middleware would be LIFO (Last In First Out). i.e. in case of the following code

func main() {
	router.Use(middleware.AccessLog)
	router.Use(middleware.CorsWrap())
}

CorsWrap would be executed first, followed by AccessLog.

Helper functions

WebGo provides a few helper functions.

  1. ResponseStatus(w http.ResponseWriter) get the HTTP status code from response writer
  2. SendHeader(w http.ResponseWriter, rCode int) - Send only an HTTP response header with the provided response code.
  3. Send(w http.ResponseWriter, contentType string, data interface{}, rCode int) - Send any response as is, with the provided content type and response code
  4. SendResponse(w http.ResponseWriter, data interface{}, rCode int) - Send a JSON response wrapped in WebGo's default response struct.
  5. SendError(w http.ResponseWriter, data interface{}, rCode int) - Send a JSON response wrapped in WebGo's default error response struct
  6. Render(w http.ResponseWriter, data interface{}, rCode int, tpl *template.Template) - Render renders a Go template, with the provided data & response code.

You can find other helper functions here.

When using Send or SendResponse, the response is wrapped in WebGo's response struct and is serialized as JSON.

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

When using SendError, the response is wrapped in WebGo's error response struct and is serialzied as JSON.

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

HTTPS ready

HTTPS server can be started easily, by providing the key & cert file. You can also have both HTTP & HTTPS servers running side by side.

Start HTTPS server

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

Starting both HTTP & HTTPS server

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

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

Graceful shutdown

Graceful shutdown lets you shutdown the server without affecting any live connections/clients connected to the server. It will complete executing all the active/live requests before shutting down.

Sample code to show how to use shutdown

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

	cfg := &webgo.Config{
		Host:            "",
		Port:            "8080",
		ReadTimeout:     15 * time.Second,
		WriteTimeout:    60 * time.Second,
		ShutdownTimeout: 15 * time.Second,
	}
	router := webgo.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 you have HTTPS server running, you can use the following code
		// err := router.ShutdownHTTPS()
		// if err != nil {
		// 	fmt.Println(err)
		// 	os.Exit(1)
		// } else {
		// 	fmt.Println("shutdown complete")
		// 	os.Exit(0)
		// }
	}()

	signal.Notify(osSig, os.Interrupt, syscall.SIGTERM)

	router.Start()

	for {
		// Prevent main thread from exiting, and wait for shutdown to complete
		time.Sleep(time.Second * 1)
	}
}

Logging

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

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

The default logger uses Go standard library's log.Logger with os.Stdout (for debug and info logs) & os.Stderr (for warning, error, fatal) as default io.Writers. You can set the io.Writer as well as disable specific types of logs using the GlobalLoggerConfig(stdout, stderr, cfgs...) function.

GlobalLoggerConfig(nil, nil, LogCfgDisableDebug, LogCfgDisableInfo...)

Usage is shown in cmd/main.go.

Usage

A fully functional sample is provided here. You can try the following API calls with the sample app.

  1. http://localhost:8080/
    • Route with no named parameters configured
  2. http://localhost:8080/matchall/
  3. `http://localhost:8080/api/
How to run the sample

If you have Go installed on your computer, open the terminal and:

$ cd $GOPATH/src
$ mkdir -p github.com/bnkamalesh
$ cd github.com/bnkamalesh
$ git clone https://github.com/bnkamalesh/webgo.git
$ cd webgo
$ go run cmd/main.go

Info 2020/06/03 12:55:26 HTTP server, listening on :8080

Or if you have Docker, open the terminal and:

$ git clone https://github.com/bnkamalesh/webgo.git
$ cd webgo
$ docker run \
-p 8080:8080 \
-v ${PWD}:/go/src/github.com/bnkamalesh/webgo/ \
-w /go/src/github.com/bnkamalesh/webgo/cmd \
--rm -ti golang:latest go run main.go

Info 2020/06/03 12:55:26 HTTP server, listening on :8080
Benchmark

You can view benchmark results at the following repos:

  1. the-benchmarker
  2. go-web-framework-benchmark
Contributing

Refer here to find out details about making a contribution

Credits

Thanks to all the contributors

The gopher

The gopher used here was created using Gopherize.me. WebGo stays out of developers' way, so sitback and enjoy a cup of coffee like this gopher.

Documentation

Overview

Package webgo is a lightweight framework for building web apps. It has a multiplexer, middleware plugging mechanism & context management of its own. The primary goal of webgo is to get out of the developer's way as much as possible. i.e. it does not enforce you to build your app in any particular pattern, instead just helps you get all the trivial things done faster and easier.

e.g. 1. Getting named URI parameters. 2. Multiplexer for regex matching of URI and such. 3. Inject special app level configurations or any such objects to 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 the key for mentioning the response header content type
	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 there's an internal server error
	ErrInternalServer = "Internal server error"
)

Variables

View Source
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)")
)

Functions

func GlobalLoggerConfig

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

GlobalLoggerConfig is used to configure the global/default logger of webgo IMPORTANT: This is not concurrent safe

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 Render404

func Render404(w http.ResponseWriter, tpl *template.Template)

Render404 - used to render a 404 page

func ResponseStatus added in v4.1.1

func ResponseStatus(rw http.ResponseWriter) int

ResponseStatus returns the response status code. It works only if the 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 in the `{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.

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 where the server has to listen for the HTTP requests
	Port string `json:"port,omitempty"`

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

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

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

	// ShutdownTimeout is the duration in which graceful shutdown is completed, in seconds
	ShutdownTimeout time.Duration
}

Config is used for reading app's configuration from json file

func (*Config) Load

func (cfg *Config) Load(filepath string)

Load 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
	// contains filtered or unexported fields
}

ContextPayload is the WebgoContext. A new instance of ContextPayload is injected inside every request's context object

func Context

func Context(r *http.Request) *ContextPayload

Context returns the ContextPayload injected inside the HTTP request context

func (*ContextPayload) Params

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

Params returns the URI parameters of the respective route

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 LOGHANDLER Logger

LOGHANDLER is a global variable which webgo uses to log messages

type Middleware

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

Middleware is the signature of WebGo'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. Note: It does not *do* a 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 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) ServeHTTP

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

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(f Middleware)

Use adds a middleware layer

func (*Router) UseOnSpecialHandlers

func (rtr *Router) UseOnSpecialHandlers(f Middleware)

UseOnSpecialHandlers adds middleware to the 2 special handlers of webgo

Directories

Path Synopsis
Package middleware defines the signature/type which can be added as a middleware to Webgo.
Package middleware defines the signature/type which can be added as a middleware to Webgo.
accesslog
Package accesslogs provides a simple straight forward access log middleware.
Package accesslogs provides a simple straight forward access log middleware.
cors
Package cors sets the appropriate CORS(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) response headers, and lets you customize.
Package cors sets the appropriate CORS(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) response headers, and lets you customize.

Jump to

Keyboard shortcuts

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