nano

package module
v1.0.0-alpha Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2020 License: MIT Imports: 15 Imported by: 0

README

Nano HTTP Multiplexer

Go Report Card GitHub issues GitHub code size in bytes GitHub

Nano is a simple & elegant HTTP multiplexer written in Go (Golang). It features REST API with Go net/http performance. If you need a minimalist, productivity, and love simplicity, Nano is great choice.

Contents

Installation

To install Nano package, you need to install Go and set your Go workspace first.

  • The first need Go installed (version 1.11+ is required), then you can use the below Go command to install Nano.
go get -u github.com/hariadivicky/nano
  • Import it in your code:
import "github.com/hariadivicky/nano"
  • Import net/http. This is optional for http status code like http.StatusOK.
import "net/http"

Quick start

# assume the following codes in example.go file
$ cat example.go
package main

import (
 "fmt"
 "net/http"

 "github.com/hariadivicky/nano"
)

func main() {
    app := nano.New()

    // simple endpoint to print hello world.
    app.GET("/", func(c *nano.Context) {
        c.String(http.StatusOK, "hello world\n")
    })

    app.Run(":8080")
}
# run example.go and visit http://localhost:8080 on your browser
$ go run example.go

API Usages

You can find a ready-to-run examples at Todo List Example.

Using HEAD, OPTIONS, GET, POST, PUT, PATCH, and DELETE
func main() {
    // Creates a nano router
    app := nano.New()

    app.HEAD("/someHead", headHandler)
    app.OPTIONS("/someOptions", optionsHandler)
    app.GET("/someGet", getHandler)
    app.POST("/somePost", postHandler)
    app.PUT("/somePut", putHandler)
    app.PATCH("/somePatch", patchHandler)
    app.DELETE("/someDelete", deleteHandler)

    // Run apps.
    app.Run(":8080")
}
Default Route Handler

You could register you own default handler, the default handler will called when there is not matching route found. If you doesn't set the default handler, nano will register default 404 response as default handler.

app.Default(func(c *nano.Context) {
    c.JSON(http.StatusNotFound, nano.H{
        "status": "error",
        "message": "Oh no, this API endpoint does not exists."
    })
})
Route Parameter

Get route parameter using c.Param(key)

func main() {
    app := nano.New()

    // This handler will match /products/1 but will not match /products/ or /products
    app.GET("/products/:productId", func(c *nano.Context) {
        productId := c.Param("productId") //string
        c.String(http.StatusOK, "you requested %s", productId)
    })
}
Static File Server

You could use *nano.Static() function to serve static files like html, css, and js in your server.

func main() {
    app := nano.New()

    assetDir := http.Dir("./statics")
    app.Static("/assets", assetDir)

    // now your static files are accessible via http://yourhost.com/assets/*
}
Request Binding

To use request binding you must provide form tag to each field in your struct. You can also add the validation rules using validate tag. to see more about available validate tag value, visit Go Validator

type Address struct {
    Street     string `form:"street" json:"street"`
    PostalCode string `form:"postal_code" json:"postal_code"`
    CityID     int    `form:"city_id" json:"city_id" validate:"required"`
}

By calling Bind function, it's will returns *nano.BindingError when an error occured due to deserialization error or validation error. The description about error fields will be stored in err.Fields.


app.GET("/address", func(c *nano.Context) {
    var address Address
    if err := c.Bind(&address); err != nil {
        c.String(err.HTTPStatusCode, err.Message)
        return
    }

    c.String(http.StatusOK, "city id: %d, postal code: %s", address.CityID, address.PostalCode)
})

The Bind function automatically choose deserialization source based on your request Content-Type and request method. GET and HEAD methods will try to bind url query or urlencoded form. Otherwise, it will try to bind multipart form or json.

but if you want to manually choose the binding source, you could uses this functions below:

Bind URL Query

If you want to bind url query like page=1&limit=50 or urlencoded form you could use BindSimpleForm

var paging Pagination
err := c.BindSimpleForm(&paging)
Bind Multipart Form

You could use BindMultipartForm to bind request body with multipart/form-data type

var post BlogPost
err := c.BindMultipartForm(&post)
Bind JSON

if you have request with application/json type, you could bind it using BindJSON function

var schema ComplexSchema
err := c.BindJSON(&schema)
Error Binding

Each you call Bind, BindSimpleForm, BindMultipartForm, and BindJSON it's always returns *nano.ErrorBinding, except when binding success without any errors it returns nil. ErrorBinding has two field that are HTTPStatusCode & Message. Here is the details:

HTTPStatusCode Reason
1 500 Conversion Error or Give non-pointer to target struct parameter
2 420 Validation Error
3 400 Deserialization Error

ErrorBinding.HTTPStatusCode is useful to determine response code

Grouping Routes

You could use grouping routes that have same prefix or using same middlewares

app := nano.New()

// simple endpoint to print hello world.
app.GET("/", func(c *nano.Context) {
    c.String(http.StatusOK, "hello world\n")
})

// path: /api/v1
apiv1 := app.Group("/api/v1")
{
    // path: /api/v1/finances
    finance := apiv1.Group("/finances")
    {
        // path: /api/v1/finances/report/1
        finance.GET("/report/:period", reportByPeriodHandler)
        // path: /api/v1/finances/expenses/voucher
        finance.POST("/expenses/voucher", createExpenseVoucherHandler)
    }

    // path: /api/v1/users
    users := apiv1.Group("/users")
    {
        // path: /api/v1/users
        users.POST("/", registerNewUserHandler)
        // path: /api/v1/users/1/detail
        users.GET("/:id/detail", showUserDetailHandler)
    }
}
Writing Middleware

Middleware implement nano.HandlerFunc, you could forward request by calling c.Next()

// LoggerMiddleware functions to log every request.
func LoggerMiddleware() nano.HandlerFunc {
    return func(c *nano.Context) {
        // before middleware.
        start := time.Now()
        log.Println(c.Method, c.Path)

        // forward to next handler.
        c.Next()

        // after middleware.
        log.Printf("request complete in %s", time.Since(start))
    }
}
Using Middleware

Using middleware on certain route

app.GET("/change-password", verifyTokenMiddleware(), changePasswordHandler)

You could chaining middleware or handler

app.GET("/secret", verifyStepOne(), verifyStepTwo(), grantAccessHandler, logChangeHandler)
Middleware Group

Using middleware on router group

app := nano.New()
// apply to all routes.
app.Use(globalMiddleware())

v1 := app.Group("/v1")
v1.Use(onlyForV1())
{
 // will apply to v1 routes.
}
Nano Context

Nano Context is wrapper for http request and response. this example will use c variable as type of *nano.Context

Request

Get request method & path

log.Println(c.Method, c.Path)

Get field value from request body

username := c.PostForm("username")

Get field value with default value from request body

status := c.PostFormDefault("status", "active")

Get url query

page := c.Query("page")

Get url query with default value

page := c.QueryDefault("page", "1")

You could check if client need JSON response

func main() {
    app := nano.New()

    // simple endpoint to print hello world.
    app.GET("/", func(c *nano.Context) {

        // client request json response.
        if c.ExpectJSON() {
            c.JSON(http.StatusOK, nano.H{
                "message": "hello world",
            })

            return
        }

        c.String(http.StatusOK, "hello world\n")
    })

    app.Run(":8080")
}
Response

Set response header & content type

c.SetHeader("X-Powered-By", "Nano HTTP Multiplexer")
c.SetContetType("image/png")

Plain text response

c.String(http.StatusNotFound, "404 not found: %s", c.Path)

JSON response (with nano object wrapper)

c.JSON(http.StatusOK, nano.H{
    "status":  "pending",
    "message": "data stored",
})

HMTL response

c.HTML(http.StatusOK, "<h1>Hello There!</h1>")

Binary response

c.Data(http.StatusOK, binaryData)

Nano Middlewares

Nano has shipped with some default middleware like cors and recovery middleware.

Recovery Middleware

Recovery middleware is functions to recover server when panic was fired.

func main() {
    app := nano.New()
    app.Use(nano.Recovery())

    app.GET("/", func(c *nano.Context) {
        stack := make([]string, 0)

        c.String(http.StatusOK, "100th stack is %s", stack[99])
    })

    app.Run(":8080")
}
CORS Middleware

This middleware is used to deal with cross-origin request.

func main() {
    app := nano.New()

    // Only allow from :3000 and google.
    cors := nano.CORSWithConfig(nano.CORSConfig{
        AllowedOrigins: []string{"http://localhost:3000", "https://wwww.google.com"},
        AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodDelete, http.MethodPut},
        AllowedHeaders: []string{nano.HeaderContentType, nano.HeaderAccept},
    })

    app.Use(cors)

    // ...
}
Gzip Middleware

Gzip middleware is used for http response compression.

func main() {
    app := nano.New()

    app.Use(nano.Gzip(gzip.DefaultCompression))

    // ...
}

don't forget to import compress/gzip package for compression level at this example. available compression levels are: gzip.NoCompression, gzip.BestSpeed, gzip.BestCompression, gzip.DefaultCompression, and gzip.HuffmanOnly

Users

Awesome project lists using Nano web framework.

License

Nano using MIT license. Please read LICENSE files for more information about nano license.

Documentation

Index

Constants

View Source
const (
	// HeaderAcceptEncoding is accept encoding.
	HeaderAcceptEncoding = "Accept-Encoding"
	// HeaderContentEncoding is content encoding.
	HeaderContentEncoding = "Content-Encoding"
	// HeaderContentLength is content length.
	HeaderContentLength = "Content-Length"
	// HeaderContentType is content type.
	HeaderContentType = "Content-Type"
	// HeaderAccept is accept content type.
	HeaderAccept = "Accept"
	// HeaderOrigin is request origin.
	HeaderOrigin = "Origin"
	// HeaderVary is request vary.
	HeaderVary = "Vary"
	// HeaderAccessControlRequestMethod is cors request method.
	HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
	// HeaderAccessControlRequestHeader is cors request header.
	HeaderAccessControlRequestHeader = "Access-Control-Request-Header"
	// HeaderAccessControlAllowOrigin is cors allowed origins.
	HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
	// HeaderAccessControlAllowMethods is cors allowed origins.
	HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
	// HeaderAccessControlAllowHeader is cors allowed headers.
	HeaderAccessControlAllowHeader = "Access-Control-Allow-Header"

	// MimeJSON is standard json mime.
	MimeJSON = "application/json"
	// MimeXML is standard json mime.
	MimeXML = "application/xml"
	// MimeHTML is standard html mime.
	MimeHTML = "text/html"
	// MimePlainText is standard plain text mime.
	MimePlainText = "text/plain"
	// MimeMultipartForm is standard multipart form mime.
	MimeMultipartForm = "multipart/form-data"
	// MimeFormURLEncoded is standard urlencoded form mime.
	MimeFormURLEncoded = "application/x-www-form-urlencoded"
)

Variables

View Source
var (
	// ErrBindNonPointer must be returned when non-pointer struct passed as targetStruct parameter.
	ErrBindNonPointer = &BindingError{
		Message:        "expected pointer to target struct, got non-pointer",
		HTTPStatusCode: http.StatusInternalServerError,
	}

	// ErrBindContentType returned when client content type besides json, urlencoded, & multipart form.
	ErrBindContentType = &BindingError{
		HTTPStatusCode: http.StatusBadRequest,
		Message:        "unknown content type of request body",
	}
)
View Source
var (
	// ErrDefaultHandler should be returned when user try to set default handler for seconds time.
	ErrDefaultHandler = errors.New("default handler already registered")
)

Functions

This section is empty.

Types

type BindingError

type BindingError struct {
	HTTPStatusCode int
	Message        string
	Fields         []string
}

BindingError is an error wrapper. HTTPStatusCode will set to 422 when there is error on validation, 400 when client sent unsupported/without Content-Type header, and 500 when targetStruct is not pointer or type conversion is fail.

type CORS

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

CORS struct.

func (*CORS) AddAllowedHeader

func (cors *CORS) AddAllowedHeader(header string)

AddAllowedHeader is functions to append method to allowed list.

func (*CORS) AddAllowedMethod

func (cors *CORS) AddAllowedMethod(method string)

AddAllowedMethod is functions to append method to allowed list.

func (*CORS) AddAllowedOrigin

func (cors *CORS) AddAllowedOrigin(origin string)

AddAllowedOrigin is functions to append method to allowed list.

func (*CORS) Handle

func (cors *CORS) Handle(c *Context)

Handle corss-origin request The Cross-Origin Resource Sharing standard works by adding new HTTP headers that allow servers to describe the set of origins that are permitted to read that information using a web browser. Additionally, for HTTP request methods that can cause side-effects on server's data (in particular, for HTTP methods other than GET, or for POST usage with certain MIME types), the specification mandates that browsers "preflight" the request, soliciting supported methods from the server with an HTTP OPTIONS request method, and then, upon "approval" from the server, sending the actual request with the actual HTTP request method. Servers can also notify clients whether "credentials" (including Cookies and HTTP Authentication data) should be sent with requests.

func (*CORS) SetAllowedHeaders

func (cors *CORS) SetAllowedHeaders(headers []string)

SetAllowedHeaders is functions to fill/replace all allowed headers.

func (*CORS) SetAllowedMethods

func (cors *CORS) SetAllowedMethods(methods []string)

SetAllowedMethods is functions to fill/replace all allowed methods.

func (*CORS) SetAllowedOrigins

func (cors *CORS) SetAllowedOrigins(origins []string)

SetAllowedOrigins is functions to fill/replace all allowed origins.

type CORSConfig

type CORSConfig struct {
	AllowedOrigins []string
	AllowedMethods []string
	AllowedHeaders []string
}

CORSConfig define nano cors middleware configuration.

type Context

type Context struct {
	Request *http.Request
	Writer  http.ResponseWriter
	Method  string
	Path    string
	Origin  string
	Params  map[string]string
	// contains filtered or unexported fields
}

Context defines nano request - response context.

func (*Context) Bind

func (c *Context) Bind(targetStruct interface{}) *BindingError

Bind request body into defined user struct. This function help you to automatic binding based on request Content-Type & request method. If you want to chooose binding method manually, you could use : BindSimpleForm to bind urlencoded form & url query, BindMultipartForm to bind multipart/form data, and BindJSON to bind application/json request body.

func (*Context) BindJSON

func (c *Context) BindJSON(targetStruct interface{}) *BindingError

BindJSON is functions to bind request body (with contet type application/json) to targetStruct. targetStruct must be pointer to user defined struct.

func (*Context) BindMultipartForm

func (c *Context) BindMultipartForm(targetStruct interface{}) *BindingError

BindMultipartForm is functions to bind request body (with contet type multipart/form-data) to targetStruct. targetStruct must be pointer to user defined struct.

func (*Context) BindSimpleForm

func (c *Context) BindSimpleForm(targetStruct interface{}) *BindingError

BindSimpleForm is functions to bind request body (with content type form-urlencoded or url query) to targetStruct. targetStruct must be pointer to user defined struct.

func (*Context) Data

func (c *Context) Data(statusCode int, binary []byte)

Data is functions to write binary response.

func (*Context) ExpectJSON

func (c *Context) ExpectJSON() bool

ExpectJSON returns true when client request json response, since this function use string.Contains, value ordering in Accept values doesn't matter.

func (*Context) File

func (c *Context) File(statusCode int, filepath string)

File will returns static file as response.

func (*Context) GetRequestHeader

func (c *Context) GetRequestHeader(key string) string

GetRequestHeader returns header value by given key.

func (*Context) HTML

func (c *Context) HTML(statusCode int, html string)

HTML is functions to write html response.

func (*Context) IsJSON

func (c *Context) IsJSON() bool

IsJSON returns true when client send json body.

func (*Context) JSON

func (c *Context) JSON(statusCode int, object interface{})

JSON is functions to write json response.

func (*Context) Next

func (c *Context) Next()

Next is functions to move cursor to the next handler stack.

func (*Context) Param

func (c *Context) Param(key string) string

Param functions is to get request parameter.

func (*Context) PostForm

func (c *Context) PostForm(key string) string

PostForm is functions to form body field.

func (*Context) PostFormDefault

func (c *Context) PostFormDefault(key string, defaultValue string) string

PostFormDefault return default value when form body field is empty.

func (*Context) Query

func (c *Context) Query(key string) string

Query is functions to get url query.

func (*Context) QueryDefault

func (c *Context) QueryDefault(key string, defaultValue string) string

QueryDefault return default value when url query is empty

func (*Context) SetContentType

func (c *Context) SetContentType(contentType string)

SetContentType is functions to set http content type response header.

func (*Context) SetHeader

func (c *Context) SetHeader(key, value string)

SetHeader is functions to set http response header.

func (*Context) Status

func (c *Context) Status(statusCode int)

Status is functions to set http status code response.

func (*Context) String

func (c *Context) String(statusCode int, template string, value ...interface{})

String is functions to write plain text response.

type Engine

type Engine struct {
	*RouterGroup
	// contains filtered or unexported fields
}

Engine defines nano web engine.

func New

func New() *Engine

New is nano constructor

func (*Engine) Run

func (ng *Engine) Run(address string) error

Run applications.

func (*Engine) ServeHTTP

func (ng *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements multiplexer.

type H

type H map[string]interface{}

H defines json wrapper.

type HandlerFunc

type HandlerFunc func(c *Context)

HandlerFunc defines nano request handler function signature.

func CORSWithConfig

func CORSWithConfig(config CORSConfig) HandlerFunc

CORSWithConfig returns cors middleware.

func Gzip

func Gzip(compressionLevel int) HandlerFunc

Gzip compression for http response. this compression works when client accept gzip in their request.

func Recovery

func Recovery() HandlerFunc

Recovery middleware is functions to recover when panic was fired.

type RouterGroup

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

RouterGroup defines collection of route that has same prefix

func (*RouterGroup) DELETE

func (rg *RouterGroup) DELETE(urlPattern string, handler HandlerFunc)

DELETE is functions to register route with DELETE request method.

func (*RouterGroup) Default

func (rg *RouterGroup) Default(handler HandlerFunc) error

Default is functions to register default handler when no matching routes. Only one Default handler allowed to register.

func (*RouterGroup) GET

func (rg *RouterGroup) GET(urlPattern string, handler ...HandlerFunc)

GET is functions to register route with GET request method.

func (*RouterGroup) Group

func (rg *RouterGroup) Group(prefix string) *RouterGroup

Group is functions to create new router group.

func (*RouterGroup) HEAD

func (rg *RouterGroup) HEAD(urlPattern string, handler ...HandlerFunc)

HEAD is functions to register route with HEAD request method.

func (*RouterGroup) OPTIONS

func (rg *RouterGroup) OPTIONS(urlPattern string, handler HandlerFunc)

OPTIONS is functions to register route with OPTIONS request method.

func (*RouterGroup) PATCH

func (rg *RouterGroup) PATCH(urlPattern string, handler HandlerFunc)

PATCH is functions to register route with PATCH request method.

func (*RouterGroup) POST

func (rg *RouterGroup) POST(urlPattern string, handler HandlerFunc)

POST is functions to register route with POST request method.

func (*RouterGroup) PUT

func (rg *RouterGroup) PUT(urlPattern string, handler HandlerFunc)

PUT is functions to register route with PUT request method.

func (*RouterGroup) Static

func (rg *RouterGroup) Static(baseURL string, rootDir http.FileSystem)

Static will create static file server.

func (*RouterGroup) Use

func (rg *RouterGroup) Use(middlewares ...HandlerFunc)

Use is functions to apply middleware function(s).

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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