Echo
Echo 是一个高性能、极简的 Go Web 框架,以其出色的性能表现、简洁的 API 设计和丰富的中间件生态而广受开发者喜爱。
简介
Echo 特性
Echo 核心特性:
- 高性能: 极快的路由速度,低内存占用
- 极简设计: 最小化核心,按需扩展
- 中间件丰富: 内置多种常用中间件
- 自动 TLS: 支持 Let's Encrypt 自动证书
- HTTP/2 支持: 原生支持 HTTP/2
- 数据绑定: 强大的数据绑定和验证
- 模板渲染: 支持多种模板引擎
- 可扩展性: 灵活的中间件和扩展机制
适用场景:
- 高性能 RESTful API
- 微服务架构
- 实时应用
- 需要极高性能的 Web 服务
- WebSocket 应用
安装 Echo
# 初始化 Go module
go mod init myproject
# 安装 Echo
go get -u github.com/labstack/echo/v4
# 安装常用中间件
go get -u github.com/labstack/echo/v4/middleware
第一个 Echo 应用
package main
import (
"github.com/labstack/echo/v4"
"net/http"
)
func main() {
// 创建 Echo 实例
e := echo.New()
// 定义路由
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, Echo!")
})
// 启动服务
e.Logger.Fatal(e.Start(":8080"))
}
核心概念
1. 路由 (Routing)
基本路由
package main
import (
"github.com/labstack/echo/v4"
"net/http"
)
func main() {
e := echo.New()
// GET 请求
e.GET("/users", getUsers)
// POST 请求
e.POST("/users", createUser)
// PUT 请求
e.PUT("/users/:id", updateUser)
// DELETE 请求
e.DELETE("/users/:id", deleteUser)
// 匿名函数处理
e.GET("/ping", func(c echo.Context) error {
return c.JSON(200, map[string]string{
"message": "pong",
})
})
e.Logger.Fatal(e.Start(":8080"))
}
func getUsers(c echo.Context) error {
return c.String(http.StatusOK, "Get all users")
}
func createUser(c echo.Context) error {
return c.String(http.StatusOK, "Create user")
}
func updateUser(c echo.Context) error {
return c.String(http.StatusOK, "Update user")
}
func deleteUser(c echo.Context) error {
return c.String(http.StatusOK, "Delete user")
}
路径参数
// 路径参数
e.GET("/users/:id", func(c echo.Context) error {
id := c.Param("id")
return c.String(http.StatusOK, "User ID: "+id)
})
// 多个路径参数
e.GET("/users/:id/posts/:postId", func(c echo.Context) error {
id := c.Param("id")
postId := c.Param("postId")
return c.String(http.StatusOK, "User: "+id+", Post: "+postId)
})
// 通配符参数
e.GET("/files/*", func(c echo.Context) error {
path := c.Param("*")
return c.String(http.StatusOK, "File path: "+path)
})
查询参数
e.GET("/search", func(c echo.Context) error {
keyword := c.QueryParam("keyword")
page := c.QueryParam("page")
// 带默认值的查询参数
limit := c.QueryParam("limit")
if limit == "" {
limit = "10"
}
return c.JSON(http.StatusOK, map[string]string{
"keyword": keyword,
"page": page,
"limit": limit,
})
})
2. 路由分组
func main() {
e := echo.New()
// API v1 路由组
v1 := e.Group("/api/v1")
v1.GET("/users", getUsers)
v1.GET("/posts", getPosts)
// API v2 路由组
v2 := e.Group("/api/v2")
v2.GET("/users", getUsersV2)
v2.GET("/posts", getPostsV2)
// 带中间件的路由组
api := e.Group("/api")
api.Use(middleware.Logger())
api.Use(middleware.Recover())
api.GET("/items", getItems)
e.Logger.Fatal(e.Start(":8080"))
}
3. 中间件 (Middleware)
内置中间件
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
// 日志中间件
e.Use(middleware.Logger())
// 恢复中间件
e.Use(middleware.Recover())
// CORS 中间件
e.Use(middleware.CORS())
// 安全头中间件
e.Use(middleware.Secure())
// 请求体大小限制
e.Use(middleware.BodyLimit("10M"))
// Gzip 压缩
e.Use(middleware.Gzip())
// 静态文件
e.Static("/static", "static")
// 自动 TLS
e.Pre(middleware.HTTPSRedirect())
e.Pre(middleware.HTTPSWWWRedirect())
e.Logger.Fatal(e.Start(":8080"))
}
自定义中间件
// 自定义中间件函数
func customMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// 请求处理前
println("Before request")
// 调用下一个处理器
err := next(c)
// 请求处理后
println("After request")
return err
}
}
// 使用自定义中间件
e.Use(customMiddleware)
认证中间件
// JWT 认证中间件
e.Use(middleware.JWT([]byte("secret")))
// 基本认证中间件
e.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
if username == "admin" && password == "password" {
return true, nil
}
return false, nil
}))
// API Key 认证中间件
func apiKeyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
apiKey := c.Request().Header.Get("X-API-Key")
if apiKey != "valid-api-key" {
return c.JSON(401, map[string]string{
"error": "Invalid API Key",
})
}
return next(c)
}
}
// 使用认证中间件
e.Use(apiKeyMiddleware)
4. 请求处理
获取请求数据
// JSON 数据绑定
type User struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"gte=0,lte=130"`
}
e.POST("/users", func(c echo.Context) error {
u := new(User)
// 绑定和验证
if err := c.Bind(u); err != nil {
return err
}
if err := c.Validate(u); err != nil {
return err
}
return c.JSON(http.StatusOK, u)
})
// 表单数据
e.POST("/form", func(c echo.Context) error {
name := c.FormValue("name")
email := c.FormValue("email")
// 获取文件
file, err := c.FormFile("file")
if err != nil {
return err
}
// 保存文件
err = c.SaveFile(file, "uploads/"+file.Filename)
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{
"name": name,
"email": email,
"file": file.Filename,
})
})
// 获取请求头
e.GET("/header", func(c echo.Context) error {
userAgent := c.Request().Header.Get("User-Agent")
authorization := c.Request().Header.Get("Authorization")
return c.JSON(http.StatusOK, map[string]string{
"user_agent": userAgent,
"authorization": authorization,
})
})
5. 响应处理
// JSON 响应
e.GET("/json", func(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{
"message": "Hello, World!",
})
})
// JSONP 响应
e.GET("/jsonp", func(c echo.Context) error {
return c.JSONP(http.StatusOK, "callback", map[string]string{
"message": "Hello, World!",
})
})
// XML 响应
e.GET("/xml", func(c echo.Context) error {
return c.XML(http.StatusOK, map[string]string{
"message": "Hello, World!",
})
})
// 字符串响应
e.GET("/string", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
// HTML 响应
e.Renderer = &Template{}
e.GET("/html", func(c echo.Context) error {
return c.Render(http.StatusOK, "hello.html", map[string]string{
"name": "Echo",
})
})
// 文件响应
e.GET("/file", func(c echo.Context) error {
return c.File("files/file.pdf")
})
// 下载文件
e.GET("/download", func(c echo.Context) error {
return c.Attachment("files/file.pdf", "download.pdf")
})
// 流式响应
e.GET("/stream", func(c echo.Context) error {
c.Response().WriteHeader(http.StatusOK)
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextPlainCharsetUTF8)
fmt.Fprintf(c.Response(), "Hello, ")
fmt.Fprintf(c.Response(), "World!")
return nil
})
// 设置响应头
e.GET("/headers", func(c echo.Context) error {
c.Response().Header().Set("Custom-Header", "value")
return c.JSON(http.StatusOK, map[string]string{
"message": "Headers set",
})
})
6. 错误处理
// 自定义错误处理器
e.HTTPErrorHandler = func(err error, c echo.Context) {
code := http.StatusInternalServerError
if he, ok := err.(*echo.HTTPError); ok {
code = he.Code
}
c.JSON(code, map[string]interface{}{
"error": err.Error(),
"code": code,
})
}
// 创建 HTTP 错误
e.GET("/error", func(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid request")
})
// 自定义错误
type CustomError struct {
Code int
Message string
}
func (e *CustomError) Error() string {
return e.Message
}
e.GET("/custom-error", func(c echo.Context) error {
return &CustomError{
Code: 400,
Message: "Custom error message",
})
}
模板渲染
import (
"html/template"
"io"
"github.com/labstack/echo/v4"
)
// 定义模板结构
type Template struct {
templates *template.Template
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func main() {
e := echo.New()
// 设置模板渲染器
t := &Template{
templates: template.Must(template.ParseGlob("templates/*.html")),
}
e.Renderer = t
// 使用模板
e.GET("/hello", func(c echo.Context) error {
return c.Render(http.StatusOK, "hello.html", map[string]string{
"name": "Echo",
})
})
e.Logger.Fatal(e.Start(":8080"))
}
静态文件服务
// 静态文件目录
e.Static("/static", "static")
// 静态文件(单文件)
e.StaticFile("/favicon.ico", "images/favicon.ico")
// 多个静态目录
e.Static("/css", "assets/css")
e.Static("/js", "assets/js")
e.Static("/images", "assets/images")
WebSocket 支持
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
e.GET("/ws", func(c echo.Context) error {
conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
if err != nil {
return err
}
defer conn.Close()
for {
// 读取消息
msgType, msg, err := conn.ReadMessage()
if err != nil {
break
}
// 发送消息
err = conn.WriteMessage(msgType, msg)
if err != nil {
break
}
}
return nil
})
数据验证
import "github.com/go-playground/validator/v10"
type User struct {
Name string `json:"name" validate:"required,min=3,max=50"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"gte=0,lte=130"`
}
e.POST("/users", func(c echo.Context) error {
u := new(User)
// 绑定和验证
if err := c.Bind(u); err != nil {
return err
}
if err := c.Validate(u); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": err.Error(),
})
}
return c.JSON(http.StatusOK, u)
})
实战示例
RESTful API
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
"strconv"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var (
users = map[int]User{
1: {ID: 1, Name: "Alice", Email: "alice@example.com"},
2: {ID: 2, Name: "Bob", Email: "bob@example.com"},
}
seq = 3
)
func main() {
e := echo.New()
// 中间件
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())
// 路由
e.GET("/users", getUsers)
e.GET("/users/:id", getUser)
e.POST("/users", createUser)
e.PUT("/users/:id", updateUser)
e.DELETE("/users/:id", deleteUser)
e.Logger.Fatal(e.Start(":8080"))
}
func getUsers(c echo.Context) error {
userList := make([]User, 0, len(users))
for _, user := range users {
userList = append(userList, user)
}
return c.JSON(http.StatusOK, userList)
}
func getUser(c echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
user, ok := users[id]
if !ok {
return c.JSON(http.StatusNotFound, map[string]string{
"error": "User not found",
})
}
return c.JSON(http.StatusOK, user)
}
func createUser(c echo.Context) error {
u := new(User)
if err := c.Bind(u); err != nil {
return err
}
u.ID = seq
seq++
users[u.ID] = *u
return c.JSON(http.StatusCreated, u)
}
func updateUser(c echo.Context) error {
u := new(User)
if err := c.Bind(u); err != nil {
return err
}
id, _ := strconv.Atoi(c.Param("id"))
users[id] = *u
return c.JSON(http.StatusOK, users[id])
}
func deleteUser(c echo.Context) error {
id, _ := strconv.Atoi(c.Param("id"))
delete(users, id)
return c.NoContent(http.StatusNoContent)
}
最佳实践
1. 项目结构
my-echo-app/
├── main.go
├── config/
│ └── config.go
├── controllers/
│ └── user_controller.go
├── models/
│ └── user.go
├── routes/
│ └── routes.go
├── middlewares/
│ └── auth.go
└── utils/
└── response.go
2. 路由组织
func setupRoutes(e *echo.Echo) {
api := e.Group("/api/v1")
api.GET("/users", handler.GetUsers)
api.GET("/users/:id", handler.GetUser)
api.POST("/users", handler.CreateUser)
api.PUT("/users/:id", handler.UpdateUser)
api.DELETE("/users/:id", handler.DeleteUser)
}
3. 配置管理
import "github.com/spf13/viper"
type Config struct {
Server struct {
Port string
}
Database struct {
Host string
Port int
User string
Password string
Name string
}
}
func loadConfig() *Config {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath("./config")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
var config Config
if err := viper.Unmarshal(&config); err != nil {
panic(err)
}
return &config
}
4. 优雅关闭
func main() {
e := echo.New()
// 设置路由
setupRoutes(e)
// 启动服务器
go func() {
if err := e.Start(":8080"); err != nil && err != http.ErrServerClosed {
e.Logger.Fatal("shutting down the server")
}
}()
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
// 优雅关闭
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := e.Shutdown(ctx); err != nil {
e.Logger.Fatal(err)
}
}
性能优化
1. 使用 Echo 路由优化
// Echo 的路由已经非常优化,直接使用即可
e.GET("/users/:id", getUser)
2. 连接池
var httpClient = &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
},
}
3. 禁用调试模式
e := echo.New()
e.Debug = false // 生产环境设置为 false
e.HideBanner = true
e.HidePort = true
常见问题
1. CORS 配置
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"http://localhost:3000"},
AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
ExposeHeaders: []string{echo.HeaderContentLength},
AllowCredentials: true,
MaxAge: 86400,
}))
2. 日志配置
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "method=${method}, uri=${uri}, status=${status}\n",
CustomTimeFormat: "2006-01-02 15:04:05",
}))
总结
Echo 是一个高性能、极简设计的 Web 框架,提供了丰富的功能和优秀的性能表现。它的中间件系统、路由设计和扩展性使得开发者能够快速构建高性能的 Web 应用。对于追求性能和简洁性的项目来说,Echo 是一个非常好的选择。