This package need >= go 1.25
- Optimized HTTP router which smartly prioritize routes.
- Build robust and scalable RESTful APIs.
- Run with standard HTTP server or FastHTTP server.
- Group APIs.
- Extensible middleware framework.
- Define middleware at root, group or route level.
- Handy functions to send variety of HTTP responses.
- Centralized HTTP error handling.
- Template rendering with multiple engine support (standard, Jet, SSE, etc).
- Define your format for the logger.
- Highly customizable.
- Business error code system.
- Form data filtering and binding.
- Transaction support.
- Reverse proxy with load balancing (random, round-robin).
- URL rewriting with reverse/unrewrite support.
- Rate limiting (with memory and Redis backends).
- Request queue for concurrency control.
- IP filtering.
- Distributed tracing (OpenTracing).
- Server-Sent Events (SSE).
$ go get github.com/webx-top/echoCreate server.go
package main
import (
"net/http"
"github.com/webx-top/echo"
"github.com/webx-top/echo/engine/standard"
)
func main() {
e := echo.New()
e.Get("/", func(c echo.Context) error {
return c.String("Hello, World!", http.StatusOK)
})
e.Run(standard.New(":1323"))
}Start server
$ go run server.goBrowse to http://localhost:1323 and you should see Hello, World! on the page.
e.Post("/users", saveUser)
e.Get("/users/:id", getUser)
e.Put("/users/:id", updateUser)
e.Delete("/users/:id", deleteUser)
e.Get("/user/<id:[\\d]+>", getUser)Support two parameter syntaxes: :param and <param:regexp>.
func getUser(c echo.Context) error {
// User ID from path `users/:id`
id := c.Param("id")
// or id := c.Paramx("id").Uint64()
}/show?team=x-men&member=wolverine
func show(c echo.Context) error {
// Get team and member from the query string
team := c.Query("team")
member := c.Query("member")
age := c.Queryx("age").Uint()
}POST /save
| name | value |
|---|---|
| name | Joe Smith |
| joe@labstack.com |
func save(c echo.Context) error {
// Get name and email
name := c.Form("name")
email := c.Form("email")
age := c.Formx("age").Uint()
}POST /save
| name | value |
|---|---|
| name | Joe Smith |
| joe@labstack.com | |
| avatar | avatar |
func save(c echo.Context) error {
// Get name and email
name := c.Form("name")
email := c.Form("email")
//------------
// Get avatar
//------------
_, err := c.SaveUploadedFile("avatar","./")
return err
}- Bind
JSONorXMLpayload into Go struct based onContent-Typerequest header. - Render response as
JSONorXMLwith status code.
type User struct {
Name string `json:"name" xml:"name"`
Email string `json:"email" xml:"email"`
}
e.Post("/users", func(c echo.Context) error {
u := new(User)
if err := c.MustBind(u); err != nil {
return err
}
return c.JSON(u, http.StatusCreated)
// or
// return c.XML(u, http.StatusCreated)
})Filter and transform form data before binding.
import "github.com/webx-top/echo/formfilter"
ff := formfilter.New()
ff.Add(
formfilter.StringToSlice("ids", ","),
formfilter.DateRange("created", "2006-01-02"),
)
// Pass filter to Bind/MustBind directly
u := new(User)
c.MustBind(u, ff.Build())Serve any file from static directory for path /static/*.
e.Use(mw.Static(&mw.StaticOptions{
Root:"static", // static file root directory
Path:"/static/", // URL path for static files
Browse:true, // show file list
}))Serve embedded files with index.html support via embed.FileSystems.
import "github.com/webx-top/echo/handler/embed"
//go:embed www/*
var wwwFS embed.FS
fs := embed.NewFileSystems()
fs.Register(wwwFS)
e.Get("/*", embed.File(fs))Serve go-bindata generated embedded static files as middleware.
import (
"github.com/admpub/go-bindata-assetfs"
"github.com/webx-top/echo/middleware/bindata"
)
//go:generate go-bindata -o=bindata_assetfs.go -pkg=main static/...
//go:generate go-bindata-assetfs -o=bindata_assetfs.go -pkg=main static/...
func NewAssetFS() *assetfs.AssetFS {
return &assetfs.AssetFS{
Asset: Asset, // generated by go-bindata
AssetDir: AssetDir, // generated by go-bindata
AssetInfo: AssetInfo, // generated by go-bindata
Prefix: "",
}
}
e.Use(bindata.Static("/static/", NewAssetFS()))Image and audio captcha support.
import "github.com/webx-top/echo/handler/captcha"
captcha.DefaultOptions.Wrapper(e)
// Browse to /captcha/<id>.png// Root level middleware
e.Use(middleware.Log())
e.Use(middleware.Recover())
// Group level middleware
g := e.Group("/admin")
g.Use(middleware.BasicAuth(func(username, password string) bool {
if username == "joe" && password == "secret" {
return true
}
return false
}))
// Route level middleware
track := func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
println("request to /users")
return next.Handle(c)
}
}
e.Get("/users", func(c echo.Context) error {
return c.String("/users", http.StatusOK)
}, track)e.Get("/setcookie", func(c echo.Context) error {
c.SetCookie("uid","1")
return c.String("/setcookie: uid="+c.GetCookie("uid"), http.StatusOK)
})Session engine supports cookie-based and file-based storage.
...
import (
...
"github.com/webx-top/echo/middleware/session"
cookieStore "github.com/webx-top/echo/middleware/session/engine/cookie"
)
...
sessionOptions := &echo.SessionOptions{
Engine: `cookie`,
Name: `SESSIONID`,
CookieOptions: &echo.CookieOptions{
Path: `/`,
Domain: ``,
MaxAge: 0,
Secure: false,
HttpOnly: true,
},
}
cookieStore.RegWithOptions(&cookieStore.CookieOptions{
KeyPairs: [][]byte{
[]byte(`123456789012345678901234567890ab`),
},
})
e.Use(session.Middleware(sessionOptions))
e.Get("/session", func(c echo.Context) error {
c.Session().Set("uid",1).Save()
return c.String(fmt.Sprintf("/session: uid=%v",c.Session().Get("uid")))
})Supports database transaction through context.
// Transaction interface
type Transaction interface {
Begin(ctx context.Context) error
Rollback(ctx context.Context) error
Commit(ctx context.Context) error
End(ctx context.Context, succeed bool) error
}
// Use in handler
c.Begin()
// ... operations
c.Commit()...
import (
...
"github.com/admpub/websocket"
"github.com/webx-top/echo"
ws "github.com/webx-top/echo/handler/websocket"
)
...
e.AddHandlerWrapper(ws.HanderWrapper)
e.Get("/websocket", func(c *websocket.Conn, ctx echo.Context) error {
//push(writer)
go func() {
var counter int
for {
if counter >= 10 {
return
}
time.Sleep(5 * time.Second)
message := time.Now().String()
ctx.Logger().Info(`Push message: `, message)
if err := c.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {
ctx.Logger().Error(`Push error: `, err.Error())
return
}
counter++
}
}()
//echo
ws.DefaultExecuter(c, ctx)
return nil
})...
import (
...
"github.com/webx-top/echo"
"github.com/admpub/sockjs-go/v3/sockjs"
ws "github.com/webx-top/echo/handler/sockjs"
)
...
options := ws.Options{
Handle: func(c sockjs.Session) error {
//push(writer)
go func() {
var counter int
for {
if counter >= 10 {
return
}
time.Sleep(5 * time.Second)
message := time.Now().String()
log.Info(`Push message: `, message)
if err := c.Send(message); err != nil {
log.Error(`Push error: `, err.Error())
return
}
counter++
}
}()
//echo
ws.DefaultExecuter(c)
return nil
},
Options: &sockjs.DefaultOptions,
Prefix: "/websocket",
}
options.Wrapper(e)import (
_ "github.com/webx-top/echo/middleware/render/sse"
"github.com/webx-top/echo/middleware/render"
)
e.Use(render.Middleware(render.New(`sse`, ``)))
e.Get("/events", func(c echo.Context) error {
return c.SSEvent("message", listener)
})Built-in reverse proxy with load balancing support (Random, Round-Robin).
import "github.com/webx-top/echo/middleware"
// Round-Robin balancer
targets := []middleware.ProxyTargeter{
&middleware.ProxyTarget{
Name: "api-1",
URL: mustParseURL("http://localhost:8081"),
},
&middleware.ProxyTarget{
Name: "api-2",
URL: mustParseURL("http://localhost:8082"),
},
}
balancer := middleware.NewRoundRobinBalancer(targets)
e.Use(middleware.Proxy(balancer))e.Use(middleware.Rewrite(map[string]string{
"/old": "/new",
"/api/*": "/$1",
"/users/:id": "/user/$1",
"/users/*/orders/*": "/user/$1/order/$2",
}))Limit concurrent request processing with queue timeout.
config := middleware.QueueConfig{
QueueSize: 100,
Workers: 10,
QueueTimeout: 30 * time.Second,
WorkerTimeout: 10 * time.Second,
}
e.Use(middleware.QueueWithConfig(config))e.Use(middleware.RequestID())
// Each response gets X-Request-ID headerimport "github.com/webx-top/echo/middleware/ipfilter"
e.Use(ipfilter.IPFilter(ipfilter.Config{
Options: ipfilter.Options{
AllowedIPs: []string{"192.168.0.0/24"},
BlockedIPs: []string{"0.0.0.0/0"},
},
}))import "github.com/webx-top/echo/middleware/opentracing"
e.Use(opentracing.Trace(tracer))package main
import (
"net/http"
"github.com/webx-top/echo"
// "github.com/webx-top/echo/engine/fasthttp"
"github.com/webx-top/echo/engine/standard"
mw "github.com/webx-top/echo/middleware"
)
func main() {
e := echo.New()
e.Use(mw.Log())
e.Get("/", func(c echo.Context) error {
return c.String("Hello, World!")
})
e.Get("/echo/:name", func(c echo.Context) error {
return c.String("Echo " + c.Param("name"))
})
e.Get("/std", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`standard net/http handleFunc`))
w.WriteHeader(200)
})
// FastHTTP
// e.Run(fasthttp.New(":4444"))
// Standard
e.Run(standard.New(":4444"))
}| Middleware | Import path | Description |
|---|---|---|
| BasicAuth | github.com/webx-top/echo/middleware | HTTP basic authentication |
| KeyAuth | github.com/webx-top/echo/middleware | API key authentication (header/query/form) |
| BodyLimit | github.com/webx-top/echo/middleware | Limit request body size |
| Gzip | github.com/webx-top/echo/middleware | Send gzip HTTP response |
| Secure | github.com/webx-top/echo/middleware | Protection against XSS, clickjacking, etc |
| CORS | github.com/webx-top/echo/middleware | Cross-Origin Resource Sharing |
| CSRF | github.com/webx-top/echo/middleware | Cross-Site Request Forgery |
| Log | github.com/webx-top/echo/middleware | Log HTTP requests |
| MethodOverride | github.com/webx-top/echo/middleware | Override request method via header |
| Recover | github.com/webx-top/echo/middleware | Recover from panics |
| HTTPSRedirect | github.com/webx-top/echo/middleware | Redirect HTTP to HTTPS |
| HTTPSWWWRedirect | github.com/webx-top/echo/middleware | Redirect HTTP to WWW HTTPS |
| WWWRedirect | github.com/webx-top/echo/middleware | Redirect non-WWW to WWW |
| NonWWWRedirect | github.com/webx-top/echo/middleware | Redirect WWW to non-WWW |
| AddTrailingSlash | github.com/webx-top/echo/middleware | Add trailing slash to URI |
| RemoveTrailingSlash | github.com/webx-top/echo/middleware | Remove trailing slash from URI |
| Static | github.com/webx-top/echo/middleware | Serve static files |
| MaxAllowed | github.com/webx-top/echo/middleware | Limit simultaneous requests |
| NoCache | github.com/webx-top/echo/middleware | Set no-cache headers |
| Rewrite | github.com/webx-top/echo/middleware | URL path rewriting |
| Proxy | github.com/webx-top/echo/middleware | Reverse proxy with load balancing |
| Queue | github.com/webx-top/echo/middleware | Request concurrency queue |
| RequestID | github.com/webx-top/echo/middleware | X-Request-ID header |
| Validate | github.com/webx-top/echo/middleware | Request validator injection |
| FuncMap | github.com/webx-top/echo/middleware | Template function map |
| AJAX | github.com/webx-top/echo/middleware | AJAX operation handler |
| Language | github.com/webx-top/echo/middleware/language | Multi-language (i18n) support |
| RateLimit | github.com/webx-top/echo/middleware/ratelimit | Rate limiting HTTP requests |
| RateLimiter | github.com/webx-top/echo/middleware/ratelimiter | Rate limiter (supports memory & Redis) |
| Session | github.com/webx-top/echo/middleware/session | Session manager |
| JWT | github.com/webx-top/echo/middleware/jwt | JWT authentication |
| Markdown | github.com/webx-top/echo/middleware/markdown | Markdown rendering |
| Render | github.com/webx-top/echo/middleware/render | HTML template rendering (standard, Jet, SSE) |
| IPFilter | github.com/webx-top/echo/middleware/ipfilter | IP address filtering |
| OpenTracing | github.com/webx-top/echo/middleware/opentracing | Distributed tracing |
| Bindata Static | github.com/webx-top/echo/middleware/bindata | Serve embedded static files |
| ReverseProxy | github.com/webx-top/reverseproxy | External reverse proxy |
| Wrapper | Import path | Description |
|---|---|---|
| Websocket | github.com/webx-top/echo/handler/websocket | Example |
| Sockjs | github.com/webx-top/echo/handler/sockjs | Example |
| Oauth2 | github.com/webx-top/echo/handler/oauth2 | Example |
| Pprof | github.com/webx-top/echo/handler/pprof | Go pprof profiler |
| Captcha | github.com/webx-top/echo/handler/captcha | Image & audio captcha |
| Embed | github.com/webx-top/echo/handler/embed | Embedded file system file server |
| SSE | github.com/webx-top/echo/middleware/render/sse | Server-Sent Events render driver |
Template Function Documentation
When using the Render middleware, the data passed to c.Render(data) is wrapped into *echo.RenderData. In Go templates, call its exported methods via $.XXX:
{{$.Now}} {{/* 当前时间 */}}
{{$.UnixTime}} {{/* 当前时间戳 */}}
{{$.Site}} {{/* 网站URL */}}
{{$.SiteRoot}} {{/* 网站根路径 */}}
{{$.URL}} {{/* 当前请求URL */}}
{{$.URI}} {{/* 当前请求URI */}}
{{$.Path}} {{/* 当前请求路径 */}}
{{$.Domain}} {{/* 当前域名 */}}
{{$.Port}} {{/* 当前端口 */}}
{{$.Scheme}} {{/* http/https */}}
{{$.Lang}} {{/* 当前语言 */}}
{{$.Referer}} {{/* 来源地址 */}}
{{$.Query "key"}} {{/* 获取查询参数 */}}
{{$.Form "key"}} {{/* 获取表单参数 */}}
{{$.Param "key"}} {{/* 获取路径参数 */}}
{{$.Get "key"}} {{/* 获取Context中存储的值 */}}
{{$.Cookie}} {{/* Cookie操作 */}}
{{$.Session}} {{/* Session操作 */}}
{{$.Flash "key"}} {{/* Flash消息 */}}
{{$.T "你好%v" "世界"}} {{/* 多语言翻译 */}}
{{$.LangURI "zh"}} {{/* 生成语言链接 */}}
{{$.URLByName "routeName" "param1"}} {{/* 根据路由名生成URL */}}
{{$.CaptchaForm}} {{/* 验证码表单 */}}
{{$.TimeAgo $time}} {{/* 时间友好显示 */}}
{{$.TsHumanize $time}}{{/* 时间区间友好显示 */}}
{{$.DurationFormat $t}}{{/* 持续时间格式化 */}}
{{$.Fetch "subtmpl" .}}{{/* 渲染子模板并嵌入 */}}
{{$.MakeURL "handler" "arg"}}{{/* 生成URL */}}
{{$.Ext}} {{/* 默认扩展名 */}}
{{$.ThemeColor}} {{/* 主题色 */}}
{{$.Prefix}} {{/* 当前路由前缀 */}}
{{$.RootPrefix}} {{/* 根路由前缀 */}}
{{$.UploadURL "subdir"}}{{/* 上传文件URL */}}
{{$.FullURL "/path"}}{{/* 生成完整URL */}}
{{$.HasAnyRequest}} {{/* 是否有任何请求数据 */}}
{{$.GetNextURL}} {{/* 获取跳转URL */}}
{{$.ReturnToCurrentURL}}{{/* 返回当前URL */}}$.Data is the original data passed to c.Render(), and $.Stored is read-only data set via c.Set("key",any).
formfilter- Form data filtering utilitiessubdomains- Subdomain routing utilities (with SafeMap)code- Business error code systemcode/register- Error code registrationencoding/dbconfig- Database configuration encodingparam- Parameter type utilities (StringSlice, StringMap, Store)testing- HTTP testing utilitiesmockcontext- Mock context for unit testingdefaults- Default configuration utilitieslogger- Logger integrations
- Vishal Rana - Author
- Hank Shen - Author
- Nitin Rana - Consultant
- Contributors