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.
Web has a simplified, linear path-matching router and supports URI definition with the following patterns:
/api/users
- URI with no dynamic values/api/users/:userID
- URI with a named parameter,
userID
- If TrailingSlash is true, a URI ending in '/' will be accepted, see sample.
- URI with a named parameter,
/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
- Named URI parameter
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 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).
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.
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.
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>"
}
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 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()
}
Web exposes a singleton & global scoped logger variable LOGHANDLER with which you can plug in your custom logger by implementing the Logger interface.
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...)
.
MDN has very good documentation on what SSE (Server-Sent Events) is.
A fully functional sample is available here.