Giter VIP home page Giter VIP logo

lion's Introduction

Lion Build Status GoDoc License

Lion is a fast HTTP router for Go with support for middlewares for building modern scalable modular REST APIs.

Lion's Hello World GIF

Features

  • Context-Aware: Lion uses the de-facto standard net/Context for storing route params and sharing variables between middlewares and HTTP handlers. It could be integrated in the standard library for Go 1.7 in 2016.
  • Modular: You can define your own modules to easily build a scalable architecture
  • REST friendly: You can define modules to groups http resources together.
  • Zero allocations: Lion generates zero garbage*.

Table of contents

Install/Update

$ go get -u github.com/celrenheit/lion

Hello World

package main

import (
	"fmt"
	"net/http"

	"github.com/celrenheit/lion"
	"golang.org/x/net/context"
)

func Home(c context.Context, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Home")
}

func Hello(c context.Context, w http.ResponseWriter, r *http.Request) {
	name := lion.Param(c, "name")
	fmt.Fprintf(w, "Hello "+name)
}

func main() {
	l := lion.Classic()
	l.GetFunc("/", Home)
	l.GetFunc("/hello/:name", Hello)
	l.Run()
}

Try it yourself by running the following command from the current directory:

$ go run examples/hello/hello.go

Getting started with modules and resources

We are going to build a sample products listing REST api (without database handling to keep it simple):

func main() {
	l := lion.Classic()
	api := l.Group("/api")
	api.Module(Products{})
	l.Run()
}

// Products module is accessible at url: /api/products
// It handles getting a list of products or creating a new product
type Products struct{}

func (p Products) Base() string {
	return "/products"
}

func (p Products) Get(c context.Context, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Fetching all products")
}

func (p Products) Post(c context.Context, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Creating a new product")
}

func (p Products) Routes(r *lion.Router) {
	// Defining a resource for getting, editing and deleting a single product
	r.Resource("/:id", OneProduct{})
}

// OneProduct resource is accessible at url: /api/products/:id
// It handles getting, editing and deleting a single product
type OneProduct struct{}

func (p OneProduct) Get(c context.Context, w http.ResponseWriter, r *http.Request) {
	id := lion.Param(c, "id")
	fmt.Fprintf(w, "Getting product: %s", id)
}

func (p OneProduct) Put(c context.Context, w http.ResponseWriter, r *http.Request) {
	id := lion.Param(c, "id")
	fmt.Fprintf(w, "Updating article: %s", id)
}

func (p OneProduct) Delete(c context.Context, w http.ResponseWriter, r *http.Request) {
	id := lion.Param(c, "id")
	fmt.Fprintf(w, "Deleting article: %s", id)
}

Try it yourself. Run:

$ go run examples/modular-hello/modular-hello.go

Open your web browser to http://localhost:3000/api/products or http://localhost:3000/api/products/123. You should see "Fetching all products" or "Getting product: 123".

Handlers

Handlers should implement the Handler interface:

type Handler interface {
	ServeHTTPC(context.Context, http.ResponseWriter, *http.Request)
}

Using Handlers

l.Get("/get", get)
l.Post("/post", post)
l.Put("/put", put)
l.Delete("/delete", delete)

Using HandlerFuncs

HandlerFuncs shoud have this function signature:

func handlerFunc(c context.Context, w http.ResponseWriter, r *http.Request)  {
  fmt.Fprintf(w, "Hi!")
}

l.GetFunc("/get", handlerFunc)
l.PostFunc("/post", handlerFunc)
l.PutFunc("/put", handlerFunc)
l.DeleteFunc("/delete", handlerFunc)

Using native http.Handler

type nativehandler struct {}

func (_ nativehandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

}

l.GetH("/somepath", nativehandler{})
l.PostH("/somepath", nativehandler{})
l.PutH("/somepath", nativehandler{})
l.DeleteH("/somepath", nativehandler{})

Using native http.Handler using lion.Wrap()

Note: using native http handler you cannot access url params.

func main() {
	l := lion.New()
	l.Get("/somepath", lion.Wrap(nativehandler{}))
}

Using native http.Handler using lion.WrapFunc()

func getHandlerFunc(w http.ResponseWriter, r *http.Request) {

}

func main() {
	l := lion.New()
	l.Get("/somepath", lion.WrapFunc(getHandlerFunc))
}

Middlewares

Middlewares should implement the Middleware interface:

type Middleware interface {
	ServeNext(Handler) Handler
}

The ServeNext function accepts a Handler and returns a Handler.

You can also use MiddlewareFuncs. For example:

func middlewareFunc(next Handler) Handler  {
	return next
}

You can also use Negroni middlewares by registering them using:

l := lion.New()
l.UseNegroni(negroni.NewRecovery())
l.Run()

Resources

You can define a resource to represent a REST, CRUD api resource. You define global middlewares using Uses() method. For defining custom middlewares for each http method, you have to create a function which name is composed of the http method suffixed by "Middlewares". For example, if you want to define middlewares for the Get method you will have to create a method called: GetMiddlewares().

A resource is defined by the following methods. Everything is optional:

// Global middlewares for the resource (Optional)
Uses() Middlewares

// Middlewares for the http methods (Optional)
GetMiddlewares() Middlewares
PostMiddlewares() Middlewares
PutMiddlewares() Middlewares
DeleteMiddlewares() Middlewares


// HandlerFuncs for each HTTP Methods (Optional)
Get(c context.Context, w http.ResponseWriter, r *http.Request)
Post(c context.Context, w http.ResponseWriter, r *http.Request)
Put(c context.Context, w http.ResponseWriter, r *http.Request)
Delete(c context.Context, w http.ResponseWriter, r *http.Request)

Example:

package main

type todolist struct{}

func (t todolist) Uses() lion.Middlewares {
	return lion.Middlewares{lion.NewLogger()}
}

func (t todolist) Get(c context.Context, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "getting todos")
}

func main() {
	l := lion.New()
	l.Resource("/todos", todolist{})
	l.Run()
}

## Modules

Modules are a way to modularize an api which can then define submodules, subresources and custom routes. A module is defined by the following methods:

// Required: Base url pattern of the module
Base() string

// Routes accepts a Router instance. This method is used to define the routes of this module.
// Each routes defined are relative to the Base() url pattern
Routes(*Router)

// Optional: Requires named middlewares. Refer to Named Middlewares section
Requires() []string
package main

type api struct{}

// Required: Base url
func (t api) Base() string { return "/api" }

// Required: Here you can declare sub-resources, submodules and custom routes.
func (t api) Routes(r *lion.Router) {
	r.Module(v1{})
	r.Get("/custom", t.CustomRoute)
}

// Optional: Attach Get method to this Module.
// ====> A Module is also a Resource.
func (t api) Get(c context.Context, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "This also a resource accessible at http://localhost:3000/api")
}

// Optional: Defining custom routes
func (t api) CustomRoute(c context.Context, w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "This a custom route for this module http://localhost:3000/api/")
}

func main() {
	l := lion.New()
	// Registering the module
	l.Module(api{})
	l.Run()
}

Examples

Using GET, POST, PUT, DELETE http methods

l := lion.Classic()

// Using Handlers
l.Get("/get", get)
l.Post("/post", post)
l.Put("/put", put)
l.Delete("/delete", delete)

// Using functions
l.GetFunc("/get", getFunc)
l.PostFunc("/post", postFunc)
l.PutFunc("/put", putFunc)
l.DeleteFunc("/delete", deleteFunc)

l.Run()

Using middlewares

func main() {
	l := lion.Classic()

	// Using middleware
	l.Use(lion.NewLogger())

	// Using middleware functions
	l.UseFunc(someMiddlewareFunc)

	l.GetFunc("/hello/:name", Hello)

	l.Run()
}

Group routes by a base path

l := lion.Classic()
api := l.Group("/api")

v1 := l.Group("/v1")
v1.GetFunc("/somepath", gettingFromV1)

v2 := l.Group("/v2")
v2.GetFunc("/somepath", gettingFromV2)

l.Run()

Mouting a router into a base path

l := lion.Classic()

sub := lion.New()
sub.GetFunc("/somepath", getting)


l.Mount("/api", sub)

Default middlewares

lion.Classic() creates a router with default middlewares (Recovery, RealIP, Logger, Static). If you wish to create a blank router without any middlewares you can use lion.New().

func main()  {
	// This a no middlewares registered
	l := lion.New()
	l.Use(lion.NewLogger())

	l.GetFunc("/hello/:name", Hello)

	l.Run()
}

Custom Middlewares

Custom middlewares should implement the Middleware interface:

type Middleware interface {
	ServeNext(Handler) Handler
}

You can also make MiddlewareFuncs to use using .UseFunc() method. It has to accept a Handler and return a Handler:

func(next Handler) Handler

Custom Logger example

type logger struct{}

func (*logger) ServeNext(next lion.Handler) lion.Handler {
	return lion.HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		next.ServeHTTPC(c, w, r)

		fmt.Printf("Served %s in %s\n", r.URL.Path, time.Since(start))
	})
}

Then in the main function you can use the middleware using:

l := lion.New()

l.Use(&logger{})
l.GetFunc("/hello/:name", Hello)
l.Run()

Benchmarks

Without path.Clean

BenchmarkLion_Param       	10000000	       164 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_Param5      	 5000000	       372 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_Param20     	 1000000	      1080 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParamWrite  	10000000	       180 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GithubStatic	10000000	       160 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GithubParam 	 5000000	       359 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GithubAll   	   30000	     62888 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlusStatic 	20000000	       104 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlusParam  	10000000	       182 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlus2Params	 5000000	       286 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlusAll    	  500000	      3227 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParseStatic 	10000000	       123 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParseParam  	10000000	       145 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_Parse2Params	10000000	       212 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParseAll    	  300000	      5242 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_StaticAll   	   50000	     37998 ns/op	       0 B/op	       0 allocs/op

With path.Clean

BenchmarkLion_Param       	10000000	       227 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_Param5      	 3000000	       427 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_Param20     	 1000000	      1321 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParamWrite  	 5000000	       256 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GithubStatic	10000000	       214 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GithubParam 	 3000000	       445 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GithubAll   	   20000	     88664 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlusStatic 	10000000	       122 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlusParam  	 5000000	       381 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlus2Params	 5000000	       409 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_GPlusAll    	  500000	      3952 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParseStatic 	10000000	       146 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParseParam  	10000000	       187 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_Parse2Params	 5000000	       314 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_ParseAll    	  200000	      7857 ns/op	       0 B/op	       0 allocs/op
BenchmarkLion_StaticAll   	   30000	     56170 ns/op	      96 B/op	       8 allocs/op

A more in depth benchmark with a comparison with other frameworks is coming soon.

License

https://github.com/celrenheit/lion/blob/master/LICENSE

Todo

  • Better static file handling
  • More documentation

Credits

lion's People

Contributors

celrenheit avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.