Zerver is a simple, scalable, restful api framework for golang.
It's mainly designed for restful api service, without session, template support, etc.. But you can still use it as a web framework by easily hack it. Documentation can be found at godoc.org, and each file contains a component, all api about this component is defined there.
go get github.com/cosiner/zerver
- RESTFul Route
- Tree-based mux/router, support route group, subrouter
- Helpful functions about request/response
- Filter(also known as middleware) Chain support
- Interceptor supported
- WebSocket support
- Task support
- Resource Marshal/Unmarshal, Pool marshaled bytes(if marshaler support)
- Request/Response Wrap
- Pluggable, lazy-initializable, removeable global components
- Predefined components/filters such as cors,compress,log,ffjson, redis etc..
package main
import (
"github.com/cosiner/zerver"
"time"
)
func main() {
server := zerver.NewServer()
server.Get("/", func(req zerver.Request, resp zerver.Response) {
resp.WriteString("Hello World!")
})
var err error
go func(server *Server, err *error) {
time.Sleep(10 * time.Millisecond)
if *err == nil {
server.Destroy()
}
}(server, &err)
err = server.Start(nil) // default listen at ":4000"
}
- resource
type User struct {
Id int `json:"id"`
Name string `json:"name"`
}
func Handle(req zerver.Request, resp zerver.Response) {
u := &User{}
req.Read(u)
resp.Send("user", u)
}
- url variables
server.Get("/user/:id", func(req zerver.Request, resp zerver.Response) {
resp.WriteString("Hello, " + req.URLVar("id"))
})
server.Get("/home/*subpath", func(req zerver.Request, resp zerver.Response) {
resp.WriteString("You access " + req.URLVar("subpath"))
})
- filter
type logger func(v ...interface{}) // it can used as ServerOption.ErrorLogger
func (log logger) Init(zerver.Enviroment) error {return nil}
func (log logger) Destroy() {}
func (log logger) Filter(req zerver.Request, resp zerver.Response, chain zerver.FilterChain) {
log(req.RemoteIP(), req.UserAgent(), req.URL().Path)
chain(req, resp) // continue the processing
}
server.Handle("/", logger(log.Println))
- interceptor
func BasicAuth(req zerver.Request, resp zerver.Response, chain zerver.FilterChain) {
user, pass := req.BasicAuth()
if user != "abc" || pass != "123" {
resp.ReportUnAuthorized()
return
}
req.SetAttr("user", user) // do before
chain(req, resp)
}
func AuthLog(req zerver.Request, resp zerver.Response, chain zerver.FilterChain) {
chain(req, resp)
req.Logger().Infoln("Auth success: ", resp.Value()) // do after
}
func Auth(req zerver.Request, resp zerver.Response) {
resp.WriteString(req.Attr("user"))
resp.SetValue(true)
}
server.Post("/auth", zerver.Intercept(
Auth, BasicAuth, AuthLog,
))
- component
env := serer.RegisterComponent(name, component)
env.SetAttr("Attr1", 100)
env.SetAttr("Attr2", "100")
comp, err := server.Component(name)
ServerOption struct {
// server listening address, default :4000
ListenAddr string
// check websocket header, default nil
WebSocketChecker HeaderChecker
// logger, default use cosiner/gohper/log.Logger with ConsoleWriter
Logger
// path variables count, suggest set as max or average, default 3
PathVarCount int
// filters count for each route, RootFilters is not include, default 5
FilterCount int
// read timeout by millseconds
ReadTimeout int
// write timeout by millseconds
WriteTimeout int
// max header bytes
MaxHeaderBytes int
// tcp keep-alive period by minutes,
// default 3, same as predefined in standard http package
KeepAlivePeriod int
// CA pem files to verify client certs
CAs []string
// ssl config, default disable tls
CertFile, KeyFile string
// if not nil, cert and key will be ignored
TLSConfig *tls.Config
}
server.Start(&ServerOption{
ListenAddr:":8000",
})
// NOTICE: Server only configured through ServerOption and NewServerWith
Server struct {
// exported fields
Router
AttrContainer
RootFilters RootFilters // root filters, Match Every Routes
ResMaster ResourceMaster // resource master, manage resource types
Log Logger // server has another method called Logger()
ComponentManager // manage global/anonymous components
}
// Enviroment is a server enviroment, real implementation is the Server itself.
// it can be accessed from Request/WebsocketConn
Enviroment interface {
Server() *Server
Logger() Logger
StartTask(path string, value interface{})
Component(name string) (interface{}, error)
}
There is only one method Handle(pattern string, i interface{})
to add component
to server(router), first parameter is the url pattern the handler process, second can be:
Router
(Created throughNewRouter()
)Handler/HandlerFunc/Literal HandlerFunc/MapHandler/MethodHandler
Filter/FilterFunc/Literal FilterFunc
WebSocketHandler/WebSocketHandlerFunc/Literal WebSocketHandler
TaskHandler/TaskHandlerFunc/Literal TaskHandlerFunc
For filter, it will be add to filters collection for this pattern. For handlers, per route should have only one handlers. For router, all routes under this section should be managed by it, you can't use Handler/Router both with same prefix.
Note: in zerver, the pattern will compile to route, they are not equal.
/user/:id/info
and /user/:name/info
is two pattern, but the same route.
ResourceMaster
manage multiple resource types you added, it's stored in Server
.
Resource
responsible for marshal/unmarshal data. JSONResource/XMLResource
already provided, and Ffjson
is also provided under components
package.
Store attribute, the server has a locked container, each request has a unlocked
container, response has only a interface{}
to store value, both used to share attributes between components.
The server's container should be used to store global attributes. The request's container should be used to pass down values between filter/filter or filter/handler. The response's container should be used to pass up value.
Example:
server.Attr(name)
server.SetAttr(name, value)
request.Attr(name)
request.SetAttr(name, value)
response.Value()
response.SetValue(value)
Feedbacks or Pull Request is welcome.
MIT.