路由方法 #
一、HTTP方法概述 #
HTTP方法定义了对资源的操作类型。Echo支持所有标准HTTP方法。
1.1 常用HTTP方法 #
| 方法 | 用途 | 是否幂等 | 是否安全 |
|---|---|---|---|
| GET | 获取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 更新资源(完整) | 是 | 否 |
| PATCH | 更新资源(部分) | 否 | 否 |
| DELETE | 删除资源 | 是 | 否 |
| HEAD | 获取响应头 | 是 | 是 |
| OPTIONS | 获取支持的方法 | 是 | 是 |
1.2 幂等性与安全性 #
text
┌─────────────────────────────────────────────────────────┐
│ HTTP方法分类 │
├─────────────────────────────────────────────────────────┤
│ │
│ 安全方法:GET、HEAD、OPTIONS │
│ ├── 多次调用不会修改资源 │
│ └── 可以被缓存 │
│ │
│ 幂等方法:GET、HEAD、OPTIONS、PUT、DELETE │
│ ├── 多次调用结果相同 │
│ └── 可以安全重试 │
│ │
│ 非幂等方法:POST、PATCH │
│ ├── 每次调用可能产生不同结果 │
│ └── 重试需要谨慎 │
│ │
└─────────────────────────────────────────────────────────┘
二、GET方法 #
2.1 基本用法 #
GET用于获取资源,参数通过URL传递。
go
e.GET("/users", func(c echo.Context) error {
users := []User{
{ID: 1, Name: "张三"},
{ID: 2, Name: "李四"},
}
return c.JSON(http.StatusOK, users)
})
2.2 获取单个资源 #
go
e.GET("/users/:id", func(c echo.Context) error {
id := c.Param("id")
user := User{ID: id, Name: "张三"}
return c.JSON(http.StatusOK, user)
})
2.3 查询参数 #
go
e.GET("/users", func(c echo.Context) error {
page := c.QueryParam("page")
size := c.QueryParam("size")
keyword := c.QueryParam("keyword")
return c.JSON(http.StatusOK, map[string]string{
"page": page,
"size": size,
"keyword": keyword,
})
})
测试:
bash
curl "http://localhost:8080/users?page=1&size=10&keyword=张"
2.4 默认值处理 #
go
e.GET("/users", func(c echo.Context) error {
page := c.QueryParam("page")
if page == "" {
page = "1"
}
size := c.QueryParam("size")
if size == "" {
size = "10"
}
return c.JSON(http.StatusOK, map[string]string{
"page": page,
"size": size,
})
})
2.5 绑定查询参数 #
go
type QueryParams struct {
Page int `query:"page"`
Size int `query:"size"`
Keyword string `query:"keyword"`
}
e.GET("/users", func(c echo.Context) error {
q := new(QueryParams)
if err := c.Bind(q); err != nil {
return err
}
return c.JSON(http.StatusOK, q)
})
三、POST方法 #
3.1 创建资源 #
POST用于创建新资源,数据通过请求体传递。
go
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
e.POST("/users", func(c echo.Context) error {
u := new(User)
if err := c.Bind(u); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
u.ID = generateID()
return c.JSON(http.StatusCreated, u)
})
测试:
bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name":"张三","email":"zhangsan@example.com"}' \
http://localhost:8080/users
3.2 表单数据 #
go
e.POST("/login", func(c echo.Context) error {
username := c.FormValue("username")
password := c.FormValue("password")
return c.JSON(http.StatusOK, map[string]string{
"username": username,
"status": "success",
})
})
测试:
bash
curl -X POST \
-d "username=admin&password=123456" \
http://localhost:8080/login
3.3 文件上传 #
go
e.POST("/upload", func(c echo.Context) error {
file, err := c.FormFile("file")
if err != nil {
return err
}
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
dst, err := os.Create(file.Filename)
if err != nil {
return err
}
defer dst.Close()
if _, err = io.Copy(dst, src); err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{
"filename": file.Filename,
"size": fmt.Sprintf("%d", file.Size),
})
})
四、PUT方法 #
4.1 完整更新 #
PUT用于完整更新资源,需要提供所有字段。
go
e.PUT("/users/:id", func(c echo.Context) error {
id := c.Param("id")
u := new(User)
if err := c.Bind(u); err != nil {
return err
}
u.ID = id
return c.JSON(http.StatusOK, u)
})
测试:
bash
curl -X PUT \
-H "Content-Type: application/json" \
-d '{"name":"李四","email":"lisi@example.com"}' \
http://localhost:8080/users/1
4.2 PUT vs POST #
| 特性 | POST | PUT |
|---|---|---|
| 用途 | 创建资源 | 更新资源 |
| 幂等性 | 否 | 是 |
| URL | 资源集合 | 具体资源 |
| 响应状态码 | 201 Created | 200 OK |
五、PATCH方法 #
5.1 部分更新 #
PATCH用于部分更新资源,只需提供修改的字段。
go
type UserUpdate struct {
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
}
e.PATCH("/users/:id", func(c echo.Context) error {
id := c.Param("id")
u := new(UserUpdate)
if err := c.Bind(u); err != nil {
return err
}
existingUser := getUserByID(id)
if u.Name != nil {
existingUser.Name = *u.Name
}
if u.Email != nil {
existingUser.Email = *u.Email
}
return c.JSON(http.StatusOK, existingUser)
})
测试:
bash
curl -X PATCH \
-H "Content-Type: application/json" \
-d '{"name":"王五"}' \
http://localhost:8080/users/1
5.2 PUT vs PATCH #
| 特性 | PUT | PATCH |
|---|---|---|
| 更新方式 | 完整替换 | 部分更新 |
| 需要字段 | 全部字段 | 仅修改字段 |
| 幂等性 | 是 | 否 |
六、DELETE方法 #
6.1 删除资源 #
go
e.DELETE("/users/:id", func(c echo.Context) error {
id := c.Param("id")
if err := deleteUser(id); err != nil {
return echo.NewHTTPError(http.StatusNotFound, "用户不存在")
}
return c.NoContent(http.StatusNoContent)
})
测试:
bash
curl -X DELETE http://localhost:8080/users/1
6.2 响应状态码 #
| 状态码 | 说明 |
|---|---|
| 204 No Content | 删除成功,无返回内容 |
| 200 OK | 删除成功,返回信息 |
| 404 Not Found | 资源不存在 |
七、HEAD方法 #
7.1 获取响应头 #
HEAD与GET类似,但只返回响应头,不返回响应体。
go
e.HEAD("/users/:id", func(c echo.Context) error {
id := c.Param("id")
c.Response().Header().Set("Content-Type", "application/json")
c.Response().Header().Set("X-Resource-Id", id)
return c.NoContent(http.StatusOK)
})
测试:
bash
curl -I http://localhost:8080/users/1
7.2 用途 #
- 检查资源是否存在
- 获取资源元信息
- 检查资源是否修改
八、OPTIONS方法 #
8.1 获取支持的方法 #
go
e.OPTIONS("/users", func(c echo.Context) error {
c.Response().Header().Set("Allow", "GET, POST, OPTIONS")
return c.NoContent(http.StatusOK)
})
8.2 CORS预检 #
Echo内置CORS中间件会自动处理OPTIONS请求:
go
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.POST, echo.PUT, echo.DELETE},
}))
九、Any和Match #
9.1 Any方法 #
注册所有HTTP方法:
go
e.Any("/api", func(c echo.Context) error {
method := c.Request().Method
return c.JSON(http.StatusOK, map[string]string{
"method": method,
})
})
9.2 Match方法 #
注册指定方法:
go
e.Match([]string{http.MethodGet, http.MethodPost}, "/api", func(c echo.Context) error {
return c.String(http.StatusOK, "GET or POST")
})
十、RESTful API设计 #
10.1 RESTful规范 #
go
func setupRoutes(e *echo.Echo) {
e.GET("/users", listUsers)
e.GET("/users/:id", getUser)
e.POST("/users", createUser)
e.PUT("/users/:id", updateUser)
e.PATCH("/users/:id", patchUser)
e.DELETE("/users/:id", deleteUser)
e.GET("/users/:id/posts", listUserPosts)
e.GET("/users/:id/posts/:postId", getUserPost)
e.POST("/users/:id/posts", createUserPost)
}
10.2 状态码规范 #
| 操作 | 成功状态码 | 失败状态码 |
|---|---|---|
| GET | 200 OK | 404 Not Found |
| POST | 201 Created | 400 Bad Request |
| PUT | 200 OK | 400, 404 |
| PATCH | 200 OK | 400, 404 |
| DELETE | 204 No Content | 404 Not Found |
10.3 完整RESTful示例 #
go
package main
import (
"net/http"
"sync"
"github.com/labstack/echo/v4"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var (
users = make(map[int]*User)
mu sync.RWMutex
nextID = 1
)
func main() {
e := echo.New()
e.GET("/users", listUsers)
e.GET("/users/:id", getUser)
e.POST("/users", createUser)
e.PUT("/users/:id", updateUser)
e.PATCH("/users/:id", patchUser)
e.DELETE("/users/:id", deleteUser)
e.Start(":8080")
}
func listUsers(c echo.Context) error {
mu.RLock()
defer mu.RUnlock()
result := make([]*User, 0, len(users))
for _, u := range users {
result = append(result, u)
}
return c.JSON(http.StatusOK, result)
}
func getUser(c echo.Context) error {
id := c.Param("id")
mu.RLock()
defer mu.RUnlock()
var userID int
fmt.Sscanf(id, "%d", &userID)
user, ok := users[userID]
if !ok {
return echo.NewHTTPError(http.StatusNotFound, "用户不存在")
}
return c.JSON(http.StatusOK, user)
}
func createUser(c echo.Context) error {
u := new(User)
if err := c.Bind(u); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
mu.Lock()
defer mu.Unlock()
u.ID = nextID
nextID++
users[u.ID] = u
return c.JSON(http.StatusCreated, u)
}
func updateUser(c echo.Context) error {
id := c.Param("id")
var userID int
fmt.Sscanf(id, "%d", &userID)
u := new(User)
if err := c.Bind(u); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
mu.Lock()
defer mu.Unlock()
if _, ok := users[userID]; !ok {
return echo.NewHTTPError(http.StatusNotFound, "用户不存在")
}
u.ID = userID
users[userID] = u
return c.JSON(http.StatusOK, u)
}
func patchUser(c echo.Context) error {
id := c.Param("id")
var userID int
fmt.Sscanf(id, "%d", &userID)
patch := make(map[string]interface{})
if err := c.Bind(&patch); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
mu.Lock()
defer mu.Unlock()
user, ok := users[userID]
if !ok {
return echo.NewHTTPError(http.StatusNotFound, "用户不存在")
}
if name, ok := patch["name"].(string); ok {
user.Name = name
}
if email, ok := patch["email"].(string); ok {
user.Email = email
}
return c.JSON(http.StatusOK, user)
}
func deleteUser(c echo.Context) error {
id := c.Param("id")
var userID int
fmt.Sscanf(id, "%d", &userID)
mu.Lock()
defer mu.Unlock()
if _, ok := users[userID]; !ok {
return echo.NewHTTPError(http.StatusNotFound, "用户不存在")
}
delete(users, userID)
return c.NoContent(http.StatusNoContent)
}
十一、总结 #
路由方法要点:
| 方法 | 用途 | 示例 |
|---|---|---|
| GET | 获取资源 | e.GET("/users", listUsers) |
| POST | 创建资源 | e.POST("/users", createUser) |
| PUT | 完整更新 | e.PUT("/users/:id", updateUser) |
| PATCH | 部分更新 | e.PATCH("/users/:id", patchUser) |
| DELETE | 删除资源 | e.DELETE("/users/:id", deleteUser) |
| HEAD | 获取头信息 | e.HEAD("/users/:id", headUser) |
| OPTIONS | 获取方法列表 | e.OPTIONS("/users", optionsUser) |
准备好学习路由参数了吗?让我们进入下一章!
最后更新:2026-03-28