• Stars
    star
    386
  • Rank 111,213 (Top 3 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 9 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐Ÿšจ Is a lightweight, fast and extensible zero allocation HTTP router for Go used to create customizable frameworks.

LARS

Project status Build Status Coverage Status Go Report Card GoDoc License Gitter

LARS is a fast radix-tree based, zero allocation, HTTP router for Go. view examples. If looking for a more pure Go solution, be sure to check out pure which is essentially a pure version of lars

Why Another HTTP Router?

Have you ever been painted into a corner by a framework, ya me too! and I've noticed that allot of routers out there, IMHO, are adding so much functionality that they are turning into Web Frameworks, (which is fine, frameworks are important) however, not at the expense of flexibility and configurability. So with no further ado, introducing LARS an HTTP router that can be your launching pad in creating a framework for your needs. How? Context is an interface see example here, where you can add as little or much as you want or need and most importantly...under your control.

Key & Unique Features

  • Context is an interface - this allows passing of framework/globals/application specific variables. example
  • Smart Route Logic - helpful logic to help prevent adding bad routes, keeping your url's consistent. i.e. /user/:id and /user/:user_id - the second one will fail to add letting you know that :user_id should be :id
  • Uber simple middleware + handlers - middleware and handlers actually have the exact same definition!
  • Custom Handlers - can register custom handlers for making other middleware + handler patterns usable with this router; the best part about this is can register one for your custom context and not have to do type casting everywhere see here
  • Diverse handler support - Full support for standard/native http Handler + HandlerFunc + some others see here
    • When Parsing a form call Context's ParseForm amd ParseMulipartForm functions and the URL params will be added into the Form object, just like query parameters are, so no extra work
  • Fast & Efficient - lars uses a custom version of httprouter so incredibly fast and efficient.

Installation

go get -u github.com/go-playground/lars

Usage

Below is a simple example, for a full example see here

package main

import (
	"fmt"
	"net/http"

	"github.com/go-playground/lars"
	mw "github.com/go-playground/lars/_examples/middleware/logging-recovery"
)

func main() {
	l := lars.New()
	// LoggingAndRecovery is just an example copy paste and modify to your needs
	l.Use(mw.LoggingAndRecovery)

	l.Get("/", HelloWorld)

	http.ListenAndServe(":3007", l.Serve())
}

// HelloWorld ...
func HelloWorld(c lars.Context) {
	c.Response().Write([]byte("Hello World"))

	// this will also work, Response() complies with http.ResponseWriter interface
	fmt.Fprint(c.Response(), "Hello World")
}

URL Params

l := l.New()

// the matching param will be stored in the Context's params with name "id"
l.Get("/user/:id", UserHandler)

// serve css, js etc.. c.Param(lars.WildcardParam) will return the remaining path if 
// you need to use it in a custom handler...
l.Get("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) 

...

Note: Since this router has only explicit matches, you can not register static routes and parameters for the same path segment. For example you can not register the patterns /user/new and /user/:user for the same request method at the same time. The routing of different request methods is independent from each other. I was initially against this, and this router allowed it in a previous version, however it nearly cost me in a big app where the dynamic param value say :type actually could have matched another static route and that's just too dangerous, so it is no longer allowed.

Groups

l.Use(LoggingAndRecovery)
...
l.Post("/users/add", ...)

// creates a group for user + inherits all middleware registered using l.Use()
user := l.Group("/user/:userid")
user.Get("", ...)
user.Post("", ...)
user.Delete("/delete", ...)

contactInfo := user.Group("/contact-info/:ciid")
contactinfo.Delete("/delete", ...)

// creates a group for others + inherits all middleware registered using l.Use() + adds 
// OtherHandler to middleware
others := l.GroupWithMore("/others", OtherHandler)

// creates a group for admin WITH NO MIDDLEWARE... more can be added using admin.Use()
admin := l.GroupWithNone("/admin")
admin.Use(SomeAdminSecurityMiddleware)
...

Custom Context + Avoid Type Casting / Custom Handlers

...
// MyContext is a custom context
type MyContext struct {
	*lars.Ctx  // a little dash of Duck Typing....
}

// CustomContextFunction is a function that is specific to your applications needs that you added
func (mc *MyContext) CustomContextFunction() {
	// do something
}

// newContext is the function that creates your custom context +
// contains lars's default context
func newContext(l *lars.LARS) lars.Context {
	return &MyContext{
		Ctx:        lars.NewContext(l),
	}
}

// casts custom context and calls you custom handler so you don;t have to type cast lars.Context everywhere
func castCustomContext(c lars.Context, handler lars.Handler) {
	// could do it in all one statement, but in long form for readability
	h := handler.(func(*MyContext))
	ctx := c.(*MyContext)

	h(ctx)
}

func main() {
	l := lars.New()
	l.RegisterContext(newContext) // all gets cached in pools for you
	l.RegisterCustomHandler(func(*MyContext) {}, castCustomContext)
	l.Use(Logger)

	l.Get("/", Home)

	http.ListenAndServe(":3007", l.Serve())
}

// Home ...notice the receiver is *MyContext, castCustomContext handled the type casting for us
// quite the time saver if you ask me.
func Home(c *MyContext) {
	c.CustomContextFunction()
	...
}

Decoding Body

For full example see here. currently JSON, XML, FORM + Multipart Form's are support out of the box.

	// first argument denotes yes or no I would like URL query parameter fields
	// to be included. i.e. 'id' in route '/user/:id' should it be included.
	// run, then change to false and you'll see user.ID is not populated.
	if err := c.Decode(true, maxBytes, &user); err != nil {
		log.Println(err)
	}

Misc

...
// can register multiple handlers, the last is considered the last in the chain and others 
// considered middleware, but just for this route and not added to middleware like l.Use() does.
l.Get(/"home", AdditionalHandler, HomeHandler)

// set custom 404 ( not Found ) handler
l.Register404(404Handler)

// Redirect to or from ending slash if route not found, default is true
l.SetRedirectTrailingSlash(true)

// Handle 405 ( Method Not allowed ), default is false
l.SetHandle405MethodNotAllowed(false)

// automatically handle OPTION requests; manually configured
// OPTION handlers take precedence. default true
l.SetAutomaticallyHandleOPTIONS(set bool)

// register custom context
l.RegisterContext(ContextFunc)

// Register custom handler type, see https://github.com/go-playground/lars/blob/master/util.go#L62
// for example handler creation
l.RegisterCustomHandler(interface{}, CustomHandlerFunc)

// NativeChainHandler is used as a helper to create your own custom handlers, or use custom handlers 
// that already exist an example usage can be found here 
// https://github.com/go-playground/lars/blob/master/util.go#L86, below is an example using nosurf CSRF middleware

l.Use(nosurf.NewPure(lars.NativeChainHandler))


// Context has 2 methods of which you should be aware of ParseForm and ParseMulipartForm, they just call the 
// default http functions but provide one more additional feature, they copy the URL params to the request 
// Forms variables, just like Query parameters would have been.
// The functions are for convenience and are totally optional.

Special Note

I don't know if it was an oversight or just an assumption about how middleware would be used with Go 1.7's new context integration into the *http.Request but there are a few quirks. As you know lars handles multiple handler types, including the native handler, this functionality is possible because of the way lar handles the middleware; lars does not chain the middleware in the normal way, but rather calles each in sequence; because of this all you have to do is call c.Next() or it has already been wrapped to do so for you transparently. OK getting back to the point, if you are not using lars.Context to set the context information you will have to set the request object so that the information gets back to the calling package. eg.

// because 'r' is a copy of a pointer to allow the information to get
// back to the caller, need to set the value of 'r' as below with '*r'
func(w http.ResponseWriter, r *http.Request) {
	*r = *r.WithContext(context.WithValue(r.Context(), 0, "testval1"))
}

this is not an issue specific to lars, but a quirk of the way context is tied to the http.Request object.

Middleware

There are some pre-defined middlewares within the middleware folder; NOTE: that the middleware inside will comply with the following rule(s):

  • Are completely reusable by the community without modification

Other middleware will be listed under the _examples/middleware/... folder for a quick copy/paste modify. as an example a logging or recovery middleware are very application dependent and therefore will be listed under the _examples/middleware/...

Benchmarks

Run on MacBook Pro (15-inch, 2017) 3.1 GHz Intel Core i7 16GB DDR3 using Go version go1.9.2 darwin/amd64

NOTICE: lars uses a custom version of httprouter, benchmarks can be found here

go test -bench=. -benchmem=true
#GithubAPI Routes: 203
   LARS: 49032 Bytes

#GPlusAPI Routes: 13
   LARS: 3640 Bytes

#ParseAPI Routes: 26
   LARS: 6632 Bytes

#Static Routes: 157
   LARS: 30120 Bytes

goos: darwin
goarch: amd64
pkg: github.com/joeybloggs/go-http-routing-benchmark
BenchmarkLARS_Param        	20000000	        51.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_Param5       	20000000	        85.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_Param20      	10000000	       215 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_ParamWrite   	20000000	        94.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_GithubStatic 	20000000	        68.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_GithubParam  	20000000	       103 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_GithubAll    	  100000	     21066 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_GPlusStatic  	30000000	        53.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_GPlusParam   	20000000	        70.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_GPlus2Params 	20000000	        84.4 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_GPlusAll     	 2000000	       894 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_ParseStatic  	20000000	        53.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_ParseParam   	20000000	        60.4 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_Parse2Params 	20000000	        68.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_ParseAll     	 1000000	      1602 ns/op	       0 B/op	       0 allocs/op
BenchmarkLARS_StaticAll    	  100000	     13777 ns/op	       0 B/op	       0 allocs/op

Package Versioning

I'm jumping on the vendoring bandwagon, you should vendor this package as I will not be creating different version with gopkg.in like allot of my other libraries.

Why? because my time is spread pretty thin maintaining all of the libraries I have + LIFE, it is so freeing not to worry about it and will help me keep pouring out bigger and better things for you the community.

This package is inspired by the following

Licenses

  • MIT License (MIT), Copyright (c) 2015 Dean Karn
  • BSD License, Copyright (c) 2013 Julien Schmidt. All rights reserved.

More Repositories

1

validator

๐Ÿ’ฏGo Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
Go
16,425
star
2

webhooks

๐ŸŽฃ Webhook receiver for GitHub, Bitbucket, GitLab, Gogs
Go
949
star
3

form

๐Ÿš‚ Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.
Go
750
star
4

pool

๐Ÿšค a limited consumer goroutine or unlimited goroutine pool for easier goroutine handling and cancellation
Go
724
star
5

universal-translator

๐Ÿ’ฌ i18n Translator for Go/Golang using CLDR data + pluralization rules
Go
375
star
6

log

๐Ÿ“— Simple, configurable and scalable Structured Logging for Go.
Go
292
star
7

locales

๐ŸŒŽ a set of locales generated from the CLDR Project which can be used independently or within an i18n package; these were built for use with, but not exclusive to https://github.com/go-playground/universal-translator
Go
267
star
8

mold

โœ‚๏ธ Is a general library to help modify or set data within data structures and other objects.
Go
225
star
9

stats

๐Ÿ“ˆ Monitors Go MemStats + System stats such as Memory, Swap and CPU and sends via UDP anywhere you want for logging etc...
Go
170
star
10

pure

๐Ÿšฑ Is a lightweight HTTP router that sticks to the std "net/http" implementation
Go
149
star
11

overalls

๐Ÿ‘–Multi-Package go project coverprofile for tools like goveralls
Go
114
star
12

statics

๐Ÿ“ Embeds static resources into go files for single binary compilation + works with http.FileSystem + symlinks
Go
68
star
13

colors

๐ŸŽจ Go color manipulation, conversion and printing library/utility
Go
67
star
14

assert

โ—Basic Assertion Library used along side native go testing, with building blocks for custom assertions
Go
62
star
15

errors

๐Ÿ’ฅError Context, Stack Trace, Types and Tags for full error handling and logging.
Go
61
star
16

kms

๐Ÿ”ช Is a library that aids in graceful shutdown of a process/application
Go
47
star
17

pkg

โญ pkg extends the core go packages with missing or additional functionality built in. All packages correspond to the std go package name with an additional suffix of `ext` to avoid naming conflicts.
Go
42
star
18

tz

Timezone Country and Zone data generated from timezonedb.com
Go
30
star
19

generate

๐Ÿƒruns go generate recursively on a specified path or environment variable and can filter by regex
Go
30
star
20

justdoit

simple auto-compile daemon that just works
Go
19
star
21

spoon

library + program to help making zero downtime, self-upgrading programs and servers.
Go
16
star
22

retry

๐Ÿ”„ Retry provides a set of standardized common components and abstracts away some code that normally is duplicated
Go
16
star
23

ansi

โฌ› ansi contains a bunch of constants and possibly additional terminal related functionality in the future.
Go
14
star
24

sensitive

provides base types who's values should never be seen by the human eye, but still used for configuration.
Go
14
star
25

ksql

a JSON data expression lexer, parser, cli and library
Go
13
star
26

backoff

:bowtie: Backoff uses an exponential backoff algorithm to backoff between retries with optional auto-tuning functionality.
Go
12
star
27

cache

Contains multiple in-memory cache implementations including LRU & LFU
Go
11
star
28

assets

Asset Pipeline for Go HTML applications
Go
8
star
29

ws

๐Ÿ™Œ ws creates a hub for WebSocket connections and abstracts away allot of the boilerplate code for managing connections using Gorilla WebSocket
Go
8
star
30

itertools

Go Iteration tools with a rusty flavour
Go
7
star
31

livereload

๐Ÿ” is an asset live-reload library that allows easy registration of path & file change monitoring and notifications for https://github.com/livereload/livereload-js
JavaScript
6
star
32

relay-client-go

This package is a Go client for the Relay Job Runner https://github.com/rust-playground/relay-rs
Go
5
star
33

backoff-sys

Bare building blocks for backing off and can be used to build more complex backoff packages
Go
4
star
34

mongostore

๐Ÿ”‘ Gorilla's session store implementation using MongoDB
Go
4
star
35

bundler

Generic Bundler to concatenate any type of files using a custom left and right delimiter, i.e. css or js files
Go
4
star
36

wave

ใ€ฐ๏ธ Package wave is a thin helper layer on top of Go's net/rpc
Go
1
star