LARS
![](https://raw.githubusercontent.com/go-playground/lars/master/_examples/README/test.gif)
![Gitter](https://badges.gitter.im/go-playground/lars.svg)
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
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()
l.Use(mw.LoggingAndRecovery)
l.Get("/", HelloWorld)
http.ListenAndServe(":3007", l.Serve())
}
func HelloWorld(c lars.Context) {
c.Response().Write([]byte("Hello World"))
fmt.Fprint(c.Response(), "Hello World")
}
URL Params
l := l.New()
l.Get("/user/:id", UserHandler)
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", ...)
user := l.Group("/user/:userid")
user.Get("", ...)
user.Post("", ...)
user.Delete("/delete", ...)
contactInfo := user.Group("/contact-info/:ciid")
contactinfo.Delete("/delete", ...)
others := l.GroupWithMore("/others", OtherHandler)
admin := l.GroupWithNone("/admin")
admin.Use(SomeAdminSecurityMiddleware)
...
Custom Context + Avoid Type Casting / Custom Handlers
...
type MyContext struct {
*lars.Ctx
}
func (mc *MyContext) CustomContextFunction() {
}
func newContext(l *lars.LARS) lars.Context {
return &MyContext{
Ctx: lars.NewContext(l),
}
}
func castCustomContext(c lars.Context, handler lars.Handler) {
h := handler.(func(*MyContext))
ctx := c.(*MyContext)
h(ctx)
}
func main() {
l := lars.New()
l.RegisterContext(newContext)
l.RegisterCustomHandler(func(*MyContext) {}, castCustomContext)
l.Use(Logger)
l.Get("/", Home)
http.ListenAndServe(":3007", l.Serve())
}
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.
if err := c.Decode(true, maxBytes, &user); err != nil {
log.Println(err)
}
Misc
...
l.Get(/"home", AdditionalHandler, HomeHandler)
l.Register404(404Handler)
l.SetRedirectTrailingSlash(true)
l.SetHandle405MethodNotAllowed(false)
l.SetAutomaticallyHandleOPTIONS(set bool)
l.RegisterContext(ContextFunc)
l.RegisterCustomHandler(interface{}, CustomHandlerFunc)
l.Use(nosurf.NewPure(lars.NativeChainHandler))
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.
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.