一、概述
1、官网
官方网站:https://echo.labstack.com
GitHub资源库:https://github.com/labstack/echo
文档:https://echo.labstack.com/docs
社区论坛:https://github.com/labstack/echo/discussions
2、安装echo
# 指定国内包源地址
go env -w GOPROXY=https://goproxy.cn
# 下载echo包
go get github.com/labstack/echo/v4
二、基本使用
1、基本语法
创建server.go
package main
import (
"net/http"
"github.com/labstack/echo/v4"
"os"
"io"
)
func main() {
e := echo.New()
// 路由
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.GET("/show", show)
e.GET("/users/:id", getUser)
e.POST("/users", saveUser)
e.PUT("/users/:id", updateUser)
e.DELETE("/users/:id", deleteUser)
// 端口
e.Logger.Fatal(e.Start(":8080"))
}
//e.GET("/show", show)
func show(c echo.Context) error {
// http://localhost:8080/show?team=x-men&member=wolverine 获取team和member参数
team := c.QueryParam("team")
member := c.QueryParam("member")
return c.String(http.StatusOK, "team:" + team + ", member:" + member)
}
// e.GET("/users/:id", getUser)
func getUser(c echo.Context) error {
// http://localhost:8080/users/zhangsan 获取path参数
id := c.Param("id")
return c.String(http.StatusOK, id)
}
// e.POST("/users", saveUser)
func saveUser(c echo.Context) error {
// 获取Form表单参数
name := c.FormValue("name")
email := c.FormValue("email")
// 获取文件
avatar, err := c.FormFile("avatar")
if err != nil {
return err
}
// Source
src, err := avatar.Open()
if err != nil {
return err
}
defer src.Close()
// Destination
dst, err := os.Create(avatar.Filename)
if err != nil {
return err
}
defer dst.Close()
// Copy 文件下载到项目根路径
if _, err = io.Copy(dst, src); err != nil {
return err
}
return c.String(http.StatusOK, "name:" + name + ", email:" + email)
}
type User struct {
Name string `json:"name" xml:"name" form:"name" query:"name"`
Email string `json:"email" xml:"email" form:"email" query:"email"`
}
// e.PUT("/users/:id", updateUser)
func updateUser(c echo.Context) error {
u := new(User)
// 根据内容类型请求头将json、xml、表单或查询参数,自动绑定到Go结构体中。
if err := c.Bind(u); err != nil {
return err
}
return c.JSON(http.StatusCreated, u)
// or
// return c.XML(http.StatusCreated, u)
}
// e.DELETE("/users/:id", deleteUser)
func deleteUser(c echo.Context) error {
// http://localhost:8080/users/zhangsan 获取path参数
id := c.Param("id")
return c.String(http.StatusOK, id)
}
执行:go run server.go
,端口8080
2、使用Bind对参数进行绑定
package main
import (
"github.com/labstack/echo/v4"
"net/http"
)
/*
可以绑定的类型:
query-查询参数
param-路径参数(也称为路由)
header-标题参数
json-请求body
xml-请求body
form-表单数据。值取自查询和请求正文。使用Go标准库格式解析。
*/
type User struct {
Name string `json:"name" form:"name" query:"name"`
Email string `json:"email" form:"email" query:"email"`
}
type UserDTO struct {
Name string
Email string
IsAdmin bool
}
func main() {
e := echo.New()
// 路由
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.GET("/show", func(c echo.Context) error {
// http://localhost:8080/show?id=123
// 查询参数的id会绑定到user的ID
var user User
err := c.Bind(&user)
if err != nil {
return c.String(http.StatusBadRequest, "bad request")
}
return c.JSON(http.StatusCreated, user)
})
e.POST("/users", func(c echo.Context) (err error) {
// 绑定user
u := new(User)
if err := c.Bind(u); err != nil {
return c.String(http.StatusBadRequest, "bad request")
}
// 为了安全起见,加载到单独的结构体中:UserDTO
user := UserDTO{
Name: u.Name,
Email: u.Email,
IsAdmin: false, // 避免公开不绑定的字段
}
// 使用user执行其他逻辑
return c.JSON(http.StatusOK, user)
})
e.Logger.Fatal(e.Start(":8080"))
}
3、响应处理
package main
import (
"github.com/labstack/echo/v4"
"net/http"
"os"
)
// User
type User struct {
Name string `json:"name" xml:"name"`
Email string `json:"email" xml:"email"`
}
func main() {
e := echo.New()
// 响应字符串
e.GET("/string", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
// 响应html
e.GET("/html", func(c echo.Context) error {
return c.HTML(http.StatusOK, "<strong>Hello, World!</strong>")
})
// 响应json
e.GET("/json1", func(c echo.Context) error {
u := &User{
Name: "张三",
Email: "1111@qq.com",
}
return c.JSON(http.StatusOK, u)
})
// 响应 格式化之后的json,传一个缩进符
e.GET("/json2", func(c echo.Context) error {
u := &User{
Name: "张三",
Email: "1111@qq.com",
}
return c.JSON(http.StatusOK, c.JSONPretty(http.StatusOK, u, " "))
})
// 响应xml
e.GET("/xml1", func(c echo.Context) error {
u := &User{
Name: "张三",
Email: "1111@qq.com",
}
return c.XML(http.StatusOK, u)
})
// 格式化 响应xml
e.GET("/xml2", func(c echo.Context) error {
u := &User{
Name: "张三",
Email: "1111@qq.com",
}
return c.XMLPretty(http.StatusOK, u, " ")
})
// 响应文件
e.GET("/file1", func(c echo.Context) error {
return c.File("C:\\Users\\Admin\\Pictures\\netty.png")
})
e.GET("/file2", func(c echo.Context) error {
return c.File("D:\\goland-2024.1.4.exe")
})
// 响应文件,附带文件名
e.GET("/file3", func(c echo.Context) error {
return c.Attachment("D:\\goland-2024.1.4.exe", "goland.exe")
})
// 响应流
e.GET("/stream", func(c echo.Context) error {
f, err := os.Open("C:\\Users\\Admin\\Pictures\\netty.png")
if err != nil {
return err
}
return c.Stream(http.StatusOK, "image/png", f)
})
// 响应空
e.GET("/none", func(c echo.Context) error {
return c.NoContent(http.StatusOK)
})
// 重定向
e.GET("/redirect", func(c echo.Context) error {
return c.Redirect(http.StatusMovedPermanently, "/string")
})
e.Logger.Fatal(e.Start(":8080"))
}
指定响应内容
type Map map[string]interface{}
func Fail(c echo.Context, code int, message string) error {
return c.JSON(200, Map{
"code": code,
"message": message,
})
}
func FailWithData(c echo.Context, code int, message string, data interface{}) error {
return c.JSON(200, Map{
"code": code,
"message": message,
"data": data,
})
}
func Success(c echo.Context, data interface{}) error {
return c.JSON(200, Map{
"code": 1,
"message": "success",
"data": data,
})
}
4、路由分组
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
)
func main() {
e := echo.New()
// 路由分组
admin := e.Group("/admin")
admin.GET("/test1", func(c echo.Context) error {
return c.String(http.StatusOK, "admin test1")
})
admin.GET("/test2", func(c echo.Context) error {
return c.String(http.StatusOK, "admin test2")
})
// 第二个路由组
user := e.Group("/user")
user.GET("/test1", func(c echo.Context) error {
return c.String(http.StatusOK, "user test1")
})
e.Logger.Fatal(e.Start(":8080"))
}
5、静态文件服务
package main
import (
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
// 指定路径,该路径下的文件会提供文件服务(参数一:前缀,参数二:本地路径)
// 访问localhost:8080/static/xxx.png 即可访问文件
e.Static("/static", "D:\\")
// 静态页面
e.File("/", "public/index.html")
// 指定文件
e.File("/favicon.ico", "images/favicon.ico")
e.Logger.Fatal(e.Start(":8080"))
}
6、Middleware:基本身份认证
package main
import (
"github.com/labstack/echo/v4"
"net/http"
)
type Map map[string]interface{}
func main() {
e := echo.New()
// 路由分组,并且该路由分组下都需要验证
admin := e.Group("/admin") // 或者直接admin := e.Group("/admin", Auth)
admin.Use(Auth)
admin.GET("/test1", func(c echo.Context) error {
return c.String(http.StatusOK, "admin test1")
})
admin.GET("/test2", func(c echo.Context) error {
return c.String(http.StatusOK, "admin test2")
})
// 第二个路由组
user := e.Group("/user")
user.GET("/test1", func(c echo.Context) error {
return c.String(http.StatusOK, "user test1")
})
e.Logger.Fatal(e.Start(":8080"))
}
func Auth(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// 校验token
if !checkToken(c) {
return c.JSON(200, Map{
"code": 401,
"message": "您的登录信息已失效,请重新登录后再试。",
})
}
return next(c)
}
}
func GetToken(c echo.Context) string {
token := c.Request().Header.Get("H-Token")
if len(token) > 0 {
return token
}
return c.QueryParam("H-Token")
}
func checkToken(c echo.Context) bool {
token := GetToken(c)
// 从redis或者其他地方校验token的有效性
if token == "user" {
return true
}
return false
}
7、使用session
package main
import (
"errors"
"fmt"
"github.com/gorilla/sessions"
"github.com/labstack/echo-contrib/session"
"github.com/labstack/echo/v4"
"log"
"net/http"
)
func main() {
e := echo.New()
e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret"))))
e.GET("/create-session", func(c echo.Context) error {
sess, err := session.Get("session", c)
if err != nil {
return err
}
sess.Options = &sessions.Options{
Path: "/",
MaxAge: 86400 * 7,
HttpOnly: true,
}
sess.Values["foo"] = "bar"
if err := sess.Save(c.Request(), c.Response()); err != nil {
return err
}
return c.NoContent(http.StatusOK)
})
e.GET("/read-session", func(c echo.Context) error {
sess, err := session.Get("session", c)
if err != nil {
return err
}
return c.String(http.StatusOK, fmt.Sprintf("foo=%v\n", sess.Values["foo"]))
})
if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}