Knot is a web server and application container for Golang Web Based App. It is still on experimental version
I've been working with Golang for sometime. While some stack are build on either command line or REST, I always use web based application as main UI. Most of the time, our application will be proxied by Nginx, and to be honest, it is complicated effort, because we have to change nginx config file and restart it whenever we have new application developed. These then inspired me to build Knot.
goget-ugithub.com/eaciit/knotIn knot, we handle function based on FnContent contract. Where contract of FnContent is as follow
varFnContentfunc(*WebContext)interface{}knot.WebContext is wrapper of KnotServer object, HttpRequest, Writer and other related context of Knot. Any data returned by this function will be used as body of the response depend on OutputType defined.
To do routing in knot, is simply by invoke Route function
funcmain(){
knot.DefaultOutputType=knot.OutputHtmlks:=new(knot.Server)
ks.Route("hi",Hi)
ks.Listen()
}
funcHi(r*knot.WebContext)interface{}{
return"Hello World!"
}Route to static folder
ks.RouteStatic("static","/Users/ariefdarmawan/Temp/knot/app1/static")Below code will run knot on locahost:13000 and add 2 web method / and /stop
package main
import (
. "github.com/eaciit/knot/knot.v1"
)
funcmain() {
ks:=new(Server)
ks.Address="localhost:13000"DefaultOutputType=OutputHtmlks.Route("/", func(wc*WebContext) interface{} {
return"Welcome to Knot Server"
})
ks.Route("/stop", func(wc*WebContext) interface{} {
deferwc.Server.Stop()
return"Server will be stopped. Bye"
})
ks.Listen()
}Controller is a struct with sets of FnContent. Knot have ability to scan registered controller for FnContent function and autoregister them
Below code will define controller called as Hello with 3 functions: Morning, Evening and Night. But since Morning and Evening are only function match with FnContent contract, those 2 functions will be registered as RouteHandler.
// here is our controllertypeHellostruct{
}
// http://servername/hello/morningfunc (h*Hello) Morning(r*knot.WebContext) interface{}{
return"Good morning"
}
// http://servername/hello/eveningfunc (h*Hello) Evening(r*knot.WebContext) interface{}{
return"Good evening"
}
func (h*Hello) Night() interface{}{
return"Good Night"
}
funcmain(){
ks:=new(knot.Server)
ks.Register(new(Hello),"")
ks.Listen()
}To run knot as Single Application Container we need to do following:
- Register application to run in Application Container
- Start the app
package main
import (
"github.com/eaciit/kingpin""github.com/eaciit/knot/knot.v1"
)
typeHellostruct{
}
func (h*Hello) Say(k*knot.WebContext) interface{}{
return"Hello World"
}
funcmain() {
app:=knot.NewApplication("")
app.DefaultOutputType=knot.OutputHtmlapp.Register(&Hello{})
knot.RegisterApp(app)
knot.StartApp(app,"localhost:12345")
}To run knot as Multiple Application Container we need to do following:
- Register application to run in Application Container
- Start the knot server
package main
import (
"github.com/eaciit/kingpin""github.com/eaciit/knot/knot.v1"// KnotApp Start// This is where we need to write down all application namespace // need to be run
_ "github.com/eaciit/knot/example/hello"// KnotApp End
)
var (
ks*knot.ServerflagAddress=kingpin.Flag("address",
"Address to be used by Knot Server. It normally formatted as SERVERNAME:PORTNUMBER").Default("localhost:9876").String()
)
funcmain() {
kingpin.Parse()
knot.DefaultOutputType=knot.OutputTemplate//--- it will run all application registered in namespaceknot.StartContainer(&knot.AppContainerConfig{
Address: *flagAddress,
})
}Now we need to create knot application that will be read by above daemon
package hello
import (
"github.com/eaciit/knot/knot.v1""github.com/eaciit/toolkit""os""time"
)
var (
//appViewsPath = "/Users/ariefdarmawan/goapp/src/github.com/eaciit/knot/example/hello/views/"appViewsPath=func() string {
d, _:=os.Getwd()
returnd
}() +"/../example/hello/views/"
)
funcinit() {
app:=knot.NewApp("Hello")
app.ViewsPath=appViewsPathapp.Register(&WorldController{})
app.Static("static", "/Users/ariefdarmawan/Temp")
app.LayoutTemplate="_template.html"knot.RegisterApp(app)
}
typeWorldControllerstruct {
}
func (w*WorldController) Say(r*knot.WebContext) interface{} {
r.Config.OutputType=knot.OutputHtmls:="<b>Hello World</b> "name:=r.Query("name")
ifname!="" {
s=s+" "+name
}
s+="</br>"returns
}
func (w*WorldController) Index(r*knot.WebContext) interface{} {
r.Config.ViewName="hello.html"return (toolkit.M{}).Set("message", "This data is sent to knot controller method")
}
...typeTestControllerstruct{
}
func (c*TestController) Session(r*knot.WebContext) interface{}{
t0:=time.Now()
visitLength:=r.Session("VisitLength", 0).(int)
newVisitLength=visitLength+int(time.Since(t0))
r.SetSession("VisitLength",newVisitLength)
returnfmt.Sprintf("Old length: %v, new length: %v", time.Duration(visitLenght), time.Duration(newVisitLength))
}typeWorldControllerstruct{
}
func (w*WorldController) Cookie(r*knot.WebContext) interface{} {
r.Config.OutputType=knot.OutputHtmlcvalue:=""cookiename:="mycookie"c, _:=r.Cookie(cookiename, "")
ifc==nil {
r.SetCookie(cookiename, "Arief Darmawan ", 30*24*time.Hour)
} else {
cvalue=c.Valuec.Value="Arief Darmawan "+time.Now().String()
c.Expires=time.Now().Add(24*30*time.Hour)
}
return"Cookie value is "+cvalue
}To enable validation
/* SetValidation enable session validation on a controller EnableDisable bool fnValidate validation process fnUrl url to be redirected if validation fail*/app.SetValidation(true, func(k*knot.WebContext)bool{
session:=k.Session("ecappsessionid")
ifsession==nil {
k.SetData("validationresult","nosession")
returnfalse
} elseif!checkSomething(session){
k.SetData("validationresult","noaccess")
returnfalse
}
returntrue
}, func(k*knot.WebContext) string {
vr:=k.Data("validationresult","").(string)
ifvr=="nosession" {
return"/home/login"
} else {
return"/page/noaccess"
}
}) and to ignore validation on specific controller
func (c*AController) MethodName(k*knot.WebContext)interface{}{
k.Config.IgnoreValidation=true...
}Below feature are available already on Knot, but not documented properly yet
- Template
- Json
