router

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 22, 2022 License: MIT Imports: 9 Imported by: 0

README

Go Reference CI CodeQL Go Report Card Coverage Status

GoLobby Router

GoLobby Router is a lightweight yet powerful HTTP router for Go programming language. It's built on top of the Go HTTP package and uses radix tree to provide the following features:

  • Routing based on HTTP method and URI
  • Route parameters and parameter patterns
  • Route wildcards
  • Middleware
  • HTTP Responses (such as JSON, XML, Text, Empty, File, and Redirect)
  • Static file serving
  • No footprint!
  • Zero-dependency!

Documentation

Required Go Version

It requires Go v1.11 or newer versions.

Installation

To install this package, run the following command in your project directory.

go get github.com/golobby/router
Quick Start

The following example demonstrates a simple example of using the router package.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    r.GET("/", func(c router.Context) error {
        // c.Request() is original http.Request
        // c.Response() is original http.ResponseWriter
        return c.Text(http.StatusOK, "Hello from GoLobby Router!")
    })
    
    r.PUT("/products/:id", func(c router.Context) error {
        return c.Text(http.StatusOK, "Update product with ID: "+c.Parameter("id"))
    })
    
    log.Fatalln(r.Start(":8000"))
}
HTTP Methods

You can use the Map() method to declare routes. It gets HTTP methods and paths (URIs). There are also some methods available for the most used HTTP methods. These methods are GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. The Any() method defines routes that handles any HTTP method.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func Handler(c router.Context) error {
    return c.Text(http.StatusOK, "Hello from GoLobby Router!")
}

func main() {
    r := router.New()
    
    r.GET("/", Handler)
    r.POST("/", Handler)
    r.PUT("/", Handler)
    r.PATCH("/", Handler)
    r.DELETE("/", Handler)
    r.HEAD("/", Handler)
    r.OPTIONS("/", Handler)
    
    r.Any("/page", Handler)
    
    r.Map("GET", "/", Handler)
    r.Map("CUSTOM", "/", Handler)
        
    log.Fatalln(r.Start(":8000"))
}
Route Parameters

To specify route parameters, prepend a colon like :id. In default, parameters could be anything but you can determine a regex pattern using the Define() method. Of course, regex patterns slow down your application, and it is recommended not to use them if possible. To catch and check route parameters in your handlers, you'll have the Parameters(), Parameter(), and HasParameter() methods.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    // "id" parameters must be numeric
    r.Define("id", "[0-9]+")
   
    // a route with one parameter
    r.GET("/posts/:id", func(c router.Context) error {
        return c.Text(http.StatusOK, c.Parameter("id"))
    })
    
    // a route with multiple parameters
    r.GET("/posts/:id/comments/:cid", func(c router.Context) error {
        return c.JSON(http.StatusOK, c.Parameters())
    })
    
    log.Fatalln(r.Start(":8000"))
}
Wildcard Routes

Wildcard routes match any URI with the specified prefix. The following example shows how it works.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    // Other routes with the same pattern should come first.
    r.GET("/pages/contact", ContactHandler)
    
    r.GET("/pages/*", PagesHandler)
    // It matches:
    // - /pages/
    // - /pages/about
    // - /pages/about/us
    // - /pages/help
    
    log.Fatalln(r.Start(":8000"))
}
Serving Static Files

The Files method is provided to serve static files directly. The example below demonstrate how to use it.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    // Other routes with the same pattern should come first.
    r.GET("/api", YourApiHandler)
    
    // The path (URI) must end with `*`.
    r.Files("/*", "./files")
    // example.com/            ==> ./files/index.html
    // example.com/photo.jpg   ==> ./files/photo.jpg
    // example.com/notes/1.txt ==> ./files/notes/1.txt
    
    log.Fatalln(r.Start(":8000"))
}
Named Routes

Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the SetName() method onto the route definition:

package main

import (
    "github.com/golobby/router"
    "github.com/golobby/router/pkg/response"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    r.GET("/", func(c router.Context) error {
        return c.Text(http.StatusOK, "I am the home!")
    }).SetName("home")
    
    r.GET("/posts/:id", func(c router.Context) error {
        return c.Text(http.StatusOK, "I am a post!")
    }).SetName("post")
    
    r.GET("/links", func(c router.Context) error {
        return c.JSON(http.StatusOK, response.M{
            "home": c.URL("home", nil), // "/"
            "post-1": c.URL("post", map[string]string{"id": "1"}), // "/posts/1"
            "post-2": c.URL("post", map[string]string{"id": "2"}), // "/posts/2"
        })
    })
    
    log.Fatalln(r.Start(":8000"))
}
Responses

The router comes with Empty, Redirect, Text, HTML, JSON, PrettyJSON, XML, PrettyXML, and Bytes responses out of the box. The examples below demonstrate how to use built-in and custom responses.

package main

import (
    "github.com/golobby/router"
    "github.com/golobby/router/pkg/response"
    "log"
    "net/http"
)

func main() {
    r := router.New()

    r.GET("/empty", func(c router.Context) error {
        return c.Empty(204)
    })

    r.GET("/redirect", func(c router.Context) error {
        return c.Redirect(301, "https://github.com/golobby/router")
    })

    r.GET("/text", func(c router.Context) error {
        return c.Text(200, "A text response")
    })

    r.GET("/html", func(c router.Context) error {
        return c.HTML(200, "<p>A HTML response</p>")
    })

    r.GET("/json", func(c router.Context) error {
        return c.JSON(200, User{"id": 13})
    })

    r.GET("/json", func(c router.Context) error {
        return c.JSON(200, response.M{"message": "Using response.M helper"})
    })

    r.GET("/json-pretty", func(c router.Context) error {
        return c.PrettyJSON(200, response.M{"message": "A pretty JSON response!"})
    })

    r.GET("/xml", func(c router.Context) error {
        return c.XML(200, User{"id": 13})
    })

    r.GET("/xml-pretty", func(c router.Context) error {
        return c.PrettyXML(200, User{"id": 13})
    })

    r.GET("/bytes", func(c router.Context) error {
        return c.Bytes(200, []bytes("Some bytes!"))
    })

    r.GET("/file", func(c router.Context) error {
	return c.File(200, "text/plain", "text.txt")
    })

    r.GET("/custom", func(c router.Context) error {
        c.Response().Header().Set("Content-Type", "text/csv")
        return c.Bytes(200, []bytes("Column 1, Column 2, Column 3"))
    })

    log.Fatalln(r.Start(":8000"))
}
Groups

You may put routes with similar attributes in groups. Currently, prefix and middleware attributes are supported.

Group by prefix

The example below demonstrates how to group routes with the same prefix.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    r.WithPrefix("/blog", func() {
        r.GET("/posts", PostsHandler)    // "/blog/posts"
        r.GET("/posts/:id", PostHandler) // "/blog/posts/:id"
        r.WithPrefix("/pages", func() {
            r.GET("/about", AboutHandler)     // "/blog/pages/about"
            r.GET("/contact", ContactHandler) // "/blog/pages/contact"
        })
    })
    
    log.Fatalln(r.Start(":8000"))
}
Group by middleware

The example below demonstrates how to group routes with the same middleware.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func AdminMiddleware(next router.Handler) router.Handler {
    return func(c router.Context) error {
        // Check user roles...
        return next(c)
    }
}

func main() {
    r := router.New()
    
    r.WithMiddleware(AdminMiddleware, func() {
        r.GET("/admin/users", UsersHandler)
        r.GET("/admin/products", ProductsHandler)
    })
    
    log.Fatalln(r.Start(":8000"))
}
Group by middlewares

The example below demonstrates how to group routes with the same middlewares.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    middlewares := []router.Middleware{Middleware1, Middleware2, Middleware3}
    r.WithMiddlewares(middlewares, func() {
        r.GET("/posts", PostsIndexHandler)
    })
    
    log.Fatalln(r.Start(":8000"))
}
Group by multiple attributes

The group() method helps you create a group of routes with the same prefix and middlewares.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    r.Group("/blog", []router.Middleware{Middleware1, Middleware2}, func() {
        r.GET("/posts", PostsHandler)
        r.GET("/posts/:id/comments", CommentsHandler)
    })
    
    log.Fatalln(r.Start(":8000"))
}
Basic Attributes

Your application might need a base prefix or global middlewares. In this case, you can set up these base attributes before defining routes.

Base prefix

The following example shows how to set a base prefix for all routes.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    // Add a prefix to all routes
    r.AddPrefix("/blog")

    r.GET("/posts", PostsHandler)
    r.GET("/posts/:id/comments", CommentsHandler)
    
    log.Fatalln(r.Start(":8000"))
}
Base middlewares

The following example shows how to set a base middlewares for all routes.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    // Add a single middleware
    r.AddMiddleware(LoggerMiddleware)
    
    // Add multiple middlewares at once
    r.AddMiddlewares([]router.Middleware{AuthMiddleware, ThrottleMiddleware})

    r.GET("/users", UsersHandler)
    r.GET("/users/:id/files", FilesHandler)
    
    log.Fatalln(r.Start(":8000"))
}
404 Handler

In default, the router returns the following HTTP 404 response when a requested URI doesn't match any route.

{"message": "Not found."}

You can set your custom handler like the following example.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    // Custom (HTML) Not Found Handler
    r.SetNotFoundHandler(func(c router.Context) error {
        return c.HTML(404, "<p>404 Not Found</p>")
    })

    r.GET("/", Handler)
    
    log.Fatalln(r.Start(":8000"))
}
Error Handling

Your handlers might return an error while processing the HTTP request. This error can be produced by your application logic or failure in the HTTP response. By default, the router logs it using Golang's built-in logger into the standard output and returns the HTTP 500 response below.

{"message": "Internal error."}

It's a good practice to add a global middleware to catch all these errors, log and handle them the way you need. The example below demonstrates how to add middleware for handling errors.

package main

import (
    "github.com/golobby/router"
    "log"
    "net/http"
)

func main() {
    r := router.New()
    
    // Error Handler
    r.AddMiddleware(func (next router.Handler) router.Handler {
        return func(c router.Context) error {
            if err := next(c); err != nil {
                myLogger.log(err)
                return c.HTML(500, "<p>Something went wrong</p>")
            }
            
            // No error will raise to the router base handler
            return nil
        }
    })

    r.GET("/", Handler)
    
    log.Fatalln(r.Start(":8000"))
}

License

GoLobby Router is released under the MIT License.

Documentation

Overview

Package router is a lightweight yet powerful HTTP router. It's built on top of the Golang HTTP package and uses the radix tree to provide routing requirements for modern applications.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Context

type Context interface {
	// Route returns the dispatched Route
	Route() *Route

	// Request returns the HTTP request.
	Request() *http.Request

	// Response return the HTTP responseWriter.
	Response() http.ResponseWriter

	// Parameters returns Route parameters.
	Parameters() map[string]string

	// Parameter returns a router parameter by name.
	Parameter(name string) string

	// HasParameter checks if router parameter exists.
	HasParameter(name string) bool

	// URL generates a URL for given route name and actual parameters.
	// It returns an empty string if it cannot find any route.
	URL(route string, parameters map[string]string) string

	// Bytes creates and sends a custom HTTP response.
	Bytes(status int, body []byte) error

	// Empty creates and sends an HTTP empty response.
	Empty(status int) error

	// Redirect creates and sends an HTTP redirection response.
	Redirect(status int, url string) error

	// Text creates and sends an HTTP text response.
	Text(status int, body string) error

	// HTML creates and sends an HTTP HTML response.
	HTML(status int, body string) error

	// JSON creates and sends an HTTP JSON response.
	JSON(status int, body interface{}) error

	// PrettyJSON creates and sends an HTTP JSON (with indents) response.
	PrettyJSON(status int, body interface{}) error

	// XML creates and sends an HTTP XML response.
	XML(status int, body interface{}) error

	// PrettyXML creates and sends an HTTP XML (with indents) response.
	PrettyXML(status int, body interface{}) error

	// File creates and sends an HTTP response that contains a file.
	File(status int, contentType, path string) error
}

Context holds the HTTP request, the HTTP responseWriter, the Route, and the Route parameters.

type DefaultContext

type DefaultContext struct {
	// contains filtered or unexported fields
}

DefaultContext is the default implementation of Context interface.

func (*DefaultContext) Bytes

func (d *DefaultContext) Bytes(status int, body []byte) error

Bytes creates and sends a custom HTTP response.

func (*DefaultContext) Empty

func (d *DefaultContext) Empty(status int) error

Empty creates and sends an HTTP empty response.

func (*DefaultContext) File added in v1.2.0

func (d *DefaultContext) File(status int, contentType, path string) error

File creates and sends an HTTP response that contains a file.

func (*DefaultContext) HTML

func (d *DefaultContext) HTML(status int, body string) error

HTML creates and sends an HTTP HTML response.

func (*DefaultContext) HasParameter

func (d *DefaultContext) HasParameter(name string) bool

HasParameter checks if router parameter exists.

func (*DefaultContext) JSON

func (d *DefaultContext) JSON(status int, body interface{}) error

JSON creates and sends an HTTP JSON response.

func (*DefaultContext) Parameter

func (d *DefaultContext) Parameter(name string) string

Parameter returns a router parameter by name.

func (*DefaultContext) Parameters

func (d *DefaultContext) Parameters() map[string]string

Parameters returns Route parameters.

func (*DefaultContext) PrettyJSON

func (d *DefaultContext) PrettyJSON(status int, body interface{}) error

PrettyJSON creates and sends an HTTP JSON (with indents) response.

func (*DefaultContext) PrettyXML

func (d *DefaultContext) PrettyXML(status int, body interface{}) error

PrettyXML creates and sends an HTTP XML (with indents) response.

func (*DefaultContext) Redirect

func (d *DefaultContext) Redirect(status int, url string) error

Redirect creates and sends an HTTP redirection response.

func (*DefaultContext) Request

func (d *DefaultContext) Request() *http.Request

Request returns the HTTP request.

func (*DefaultContext) Response

func (d *DefaultContext) Response() http.ResponseWriter

Response return the HTTP responseWriter.

func (*DefaultContext) Route

func (d *DefaultContext) Route() *Route

Route returns the dispatched Route

func (*DefaultContext) Text

func (d *DefaultContext) Text(status int, body string) error

Text creates and sends an HTTP text response.

func (*DefaultContext) URL added in v1.1.0

func (d *DefaultContext) URL(route string, parameters map[string]string) string

URL generates a URL for given route name and actual parameters. It returns an empty string if it cannot find any route.

func (*DefaultContext) XML

func (d *DefaultContext) XML(status int, body interface{}) error

XML creates and sends an HTTP XML response.

type Handler

type Handler func(c Context) error

Handler is an interface for Route handlers (controllers). When the router finds a Route for the incoming HTTP request, it calls the Route's handler.

type Middleware

type Middleware func(next Handler) Handler

Middleware is an interface for Route middlewares. It returns a Handler that receives HTTP Context to watch or manipulate and calls the next middlewares/handler.

type Route

type Route struct {
	// contains filtered or unexported fields
}

Route holds Route information.

func (*Route) Method

func (r *Route) Method() string

Method returns route method.

func (*Route) Name added in v1.1.0

func (r *Route) Name() string

Name returns route method.

func (*Route) Path

func (r *Route) Path() string

Path returns route method.

func (*Route) SetName added in v1.1.0

func (r *Route) SetName(name string)

SetName sets/updates route name.

func (*Route) URL added in v1.1.0

func (r *Route) URL(parameters map[string]string) string

URL generate URL from route path with given parameters.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router is the entry point of the package. It gets routes, Route parameter patterns, and middlewares then dispatches them. It receives HTTP requests, finds the related Route then runs the Route handler through its middlewares.

func New

func New() *Router

New creates a new Router instance.

func (Router) AddMiddleware

func (r Router) AddMiddleware(middleware Middleware)

AddMiddleware adds a global middlewares for next or all routes.

func (Router) AddMiddlewares

func (r Router) AddMiddlewares(middlewares []Middleware)

AddMiddlewares adds set of global middlewares for next or all routes.

func (Router) AddPrefix

func (r Router) AddPrefix(prefix string)

AddPrefix adds a global content for next or all routes.

func (Router) Any added in v1.3.0

func (r Router) Any(path string, handler Handler) *Route

Any maps a method-agnostic Route.

func (Router) DELETE

func (r Router) DELETE(path string, handler Handler) *Route

DELETE maps a DELETE Route.

func (Router) Define

func (r Router) Define(parameter, pattern string)

Define assigns a regular expression pattern to a Route parameter. After the definition, the router only dispatches the related Route if the request URI matches the pattern.

func (Router) Files added in v1.2.1

func (r Router) Files(path, directory string) *Route

Files defines a new static file server on the given path (URI) for the given directory root. The path (URI) must end with `*` to cover all the existing files and subdirectories.

func (Router) GET

func (r Router) GET(path string, handler Handler) *Route

GET maps a GET Route.

func (Router) Group

func (r Router) Group(prefix string, middleware []Middleware, body func())

Group creates a group of routes with common attributes. Currently, content and middlewares attributes are supported.

func (Router) HEAD

func (r Router) HEAD(path string, handler Handler) *Route

HEAD maps a HEAD Route.

func (Router) Map

func (r Router) Map(method, path string, handler Handler) *Route

Map defines a new Route by HTTP method and path and assigns a handler. The path (URI) may contain Route parameters.

func (Router) OPTIONS

func (r Router) OPTIONS(path string, handler Handler) *Route

OPTIONS maps a OPTIONS Route.

func (Router) PATCH

func (r Router) PATCH(path string, handler Handler) *Route

PATCH maps a PATCH Route.

func (Router) POST

func (r Router) POST(path string, handler Handler) *Route

POST maps a POST Route.

func (Router) PUT

func (r Router) PUT(path string, handler Handler) *Route

PUT maps a PUT Route.

func (Router) Serve

func (r Router) Serve(rw http.ResponseWriter, request *http.Request)

Serve handles the request manually with a given request and a response writer.

func (Router) SetNotFoundHandler

func (r Router) SetNotFoundHandler(handler Handler)

SetNotFoundHandler receives a handler and runs it when user request won't lead to any declared Route. It is the application 404 error handler, indeed.

func (Router) Start

func (r Router) Start(address string) error

Start runs the HTTP listener and waits for HTTP requests. It should be called after definitions of routes.

func (Router) WithMiddleware

func (r Router) WithMiddleware(middleware Middleware, body func())

WithMiddleware creates a group of routes with common middlewares.

func (Router) WithMiddlewares

func (r Router) WithMiddlewares(middleware []Middleware, body func())

WithMiddlewares creates a group of routes with common set of middlewares.

func (Router) WithPrefix

func (r Router) WithPrefix(prefix string, body func())

WithPrefix creates a group of routes with common content.

Directories

Path Synopsis
pkg

Jump to

Keyboard shortcuts

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