Box is an HTTP router to speed up development. Box supports URL parameters, interceptors, magic handlers and introspection documentation.
package main
import (
"github.com/fulldump/box"
)
funcmain() {
b:=box.NewBox()
b.HandleFunc("GET", "/hello", func(w http.ResponseWriter, r*http.Request) {
w.Write([]byte("World!"))
})
b.ListenAndServe() // listening at http://localhost:8080
}b:=box.NewBox()
typeMyResponsestruct {
NamestringAgeint
}
b.Handle("GET", "/hello", func(w http.ResponseWriter, r*http.Request) MyResponse {
returnMyResponse{
Name: "Fulanez",
Age: 33,
}
})b:=box.NewBox()
b.Handle("GET", "/articles/{article-id}", func(w http.ResponseWriter, r*http.Request) string {
articleID:=r.PathValue("article-id")
return"ArticleID is "+articleID
})Or before go 1.22:
articleID:=box.Param(r, "article-id")typeCreateArticleRequeststruct {
TitlestringTextstring
}
typeArticlestruct {
Idstring`json:"id"`Titlestring`json:"title"`Textstring`json:"text"`Created time.Time`json:"created"`
}
b:=box.NewBox()
b.Handle("POST", "/articles", func(inputCreateArticleRequest) Article {
fmt.Println("Persist new article...", input)
returnArticle{
Id: "my-new-id",
Title: input.Title,
Text: input.Text,
Created: time.Unix(1674762079, 0),
}
})Interceptors, also known as middlewares, are pieces of code that are executed in order before the handler to provide common functionality:
- Do things before and/or after the handler execution
- Cut the execution and stop executing the rest of interceptors and handler
- Inject items into the context
funcListArticles() { /* ... */ }
funcCreateArticles() { /* ... */ }
funcGetArticle() { /* ... */ }
funcDeleteArticle() { /* ... */ }
funcmain() {
b:=box.NewBox()
b.Use(box.AccessLog) // use middlewares to print logsb.Use(box.PrettyError) // use middlewares return pretty errorsb.Handle("GET", "/articles", ListArticles)
b.Handle("POST", "/articles", CreateArticles)
b.Handle("GET", "/articles/{article-id}", GetArticle)
b.Handle("DELETE", "/articles/{article-id}", DeleteArticle)
}b:=box.NewBox()
b.Use(box.PrettyError)
b.Handle("GET", "/articles", func() (*Article, error) {
returnnil, errors.New("could not connect to the database")
})
gob.ListenAndServe()
resp, _:=http.Get(s.URL+"/articles")
io.Copy(os.Stdout, resp.Body) // could not connect to the databaseGroups are a neat way to organize and compose big APIs and also to limit the scope of interceptors.
b:=box.NewBox()
v0:=b.Group("/v0")
v0.Use(box.SetResponseHeader("Content-Type", "application/json"))
v0.Handle("GET", "/articles", ListArticles)
v0.Handle("POST", "/articles", CreateArticle)Interceptors are very useful to reuse logic in a very convenient and modular way.
Here is a sample interceptor that does nothing:
funcMyCustomInterceptor(next box.H) box.H {
returnfunc(ctx context.Context) {
// do something before the handlernext(ctx) // continue the flow// do something after the handler
}
}The following interceptor returns a Server header:
funcMyCustomInterceptor(next box.H) box.H {
returnfunc(ctx context.Context) {
w:=box.GetResponse(ctx)
w.Header().Set("Server", "MyServer")
next(ctx) // continue the flow
}
}
funcmain() {
b:=box.NewBox()
b.Use(MyCustomInterceptor)
}Sometimes interceptors can be generalized to cover a wider set of use cases. For example, the following interceptor can set any response header and can be used multiple times.
funcSetResponseHeader(key, valuestring) box.I {
returnfunc(next box.H) box.H {
returnfunc(ctx context.Context) {
box.GetResponse(ctx).Header().Set(key, value)
next(ctx)
}
}
}
funcmain() {
b:=box.NewBox()
b.Use(
box.SetResponseHeader("Server", "My server name"),
box.SetResponseHeader("Version", "v3.2.1"),
)
}Leverage all the API information you have already defined with Box to generate your OpenAPI specification, including your types.
Just use the function boxopenapi.Spec and publish your spec:
funcmain() {
b:=box.NewBox()
// ... define all your handlersspec:=boxopenapi.Spec(b)
spec.Info.Title="My service"spec.Info.Version="1.0"spec.Servers= []boxopenapi.Server{
{
Url: "http://localhost:8080",
},
}
b.Handle("GET", "/openapi.json", func() any {
returnspec
})
}