请求处理 #

一、请求对象 #

1.1 获取Request #

go
e.GET("/", func(c echo.Context) error {
    req := c.Request()
    
    return c.JSON(http.StatusOK, map[string]string{
        "method": req.Method,
        "host":   req.Host,
        "url":    req.URL.String(),
    })
})

1.2 Request常用属性 #

属性 说明
Method HTTP方法
URL 请求URL
Header 请求头
Body 请求体
ContentLength 内容长度
Host 主机名
RemoteAddr 远程地址

二、路径参数 #

2.1 单个参数 #

go
e.GET("/users/:id", func(c echo.Context) error {
    id := c.Param("id")
    return c.String(http.StatusOK, "User ID: "+id)
})

2.2 多个参数 #

go
e.GET("/users/:userId/posts/:postId", func(c echo.Context) error {
    userId := c.Param("userId")
    postId := c.Param("postId")
    
    return c.JSON(http.StatusOK, map[string]string{
        "userId": userId,
        "postId": postId,
    })
})

2.3 通配符参数 #

go
e.GET("/files/*", func(c echo.Context) error {
    path := c.Param("*")
    return c.String(http.StatusOK, "File: "+path)
})

三、查询参数 #

3.1 单个参数 #

go
e.GET("/search", func(c echo.Context) error {
    q := c.QueryParam("q")
    return c.String(http.StatusOK, "Search: "+q)
})

3.2 多个参数 #

go
e.GET("/users", func(c echo.Context) error {
    page := c.QueryParam("page")
    size := c.QueryParam("size")
    sort := c.QueryParam("sort")
    
    return c.JSON(http.StatusOK, map[string]string{
        "page": page,
        "size": size,
        "sort": sort,
    })
})

3.3 默认值 #

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,
    })
})

3.4 所有查询参数 #

go
e.GET("/filter", func(c echo.Context) error {
    params := c.QueryParams()
    
    result := make(map[string][]string)
    for key, values := range params {
        result[key] = values
    }
    
    return c.JSON(http.StatusOK, result)
})

3.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)
})

四、请求头 #

4.1 获取单个请求头 #

go
e.GET("/headers", func(c echo.Context) error {
    auth := c.Request().Header.Get("Authorization")
    contentType := c.Request().Header.Get("Content-Type")
    
    return c.JSON(http.StatusOK, map[string]string{
        "authorization": auth,
        "content-type":  contentType,
    })
})

4.2 获取所有请求头 #

go
e.GET("/headers/all", func(c echo.Context) error {
    headers := make(map[string][]string)
    for key, values := range c.Request().Header {
        headers[key] = values
    }
    
    return c.JSON(http.StatusOK, headers)
})

4.3 常用请求头 #

go
e.GET("/info", func(c echo.Context) error {
    userAgent := c.Request().UserAgent()
    referer := c.Request().Header.Get("Referer")
    accept := c.Request().Header.Get("Accept")
    acceptLanguage := c.Request().Header.Get("Accept-Language")
    
    return c.JSON(http.StatusOK, map[string]string{
        "userAgent":      userAgent,
        "referer":        referer,
        "accept":         accept,
        "acceptLanguage": acceptLanguage,
    })
})

五、请求体 #

5.1 表单数据 #

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,
    })
})

5.2 多值表单 #

go
e.POST("/tags", func(c echo.Context) error {
    tags := c.Request().Form["tags"]
    
    return c.JSON(http.StatusOK, map[string][]string{
        "tags": tags,
    })
})

5.3 JSON数据 #

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 err
    }
    
    return c.JSON(http.StatusCreated, u)
})

5.4 读取原始请求体 #

go
e.POST("/raw", func(c echo.Context) error {
    body, err := io.ReadAll(c.Request().Body)
    if err != nil {
        return err
    }
    
    return c.String(http.StatusOK, string(body))
})

5.5 XML数据 #

go
type User struct {
    XMLName xml.Name `xml:"user"`
    Name    string   `xml:"name"`
    Email   string   `xml:"email"`
}

e.POST("/xml", func(c echo.Context) error {
    u := new(User)
    if err := xml.NewDecoder(c.Request().Body).Decode(u); err != nil {
        return err
    }
    
    return c.XML(http.StatusCreated, u)
})

六、文件上传 #

6.1 单文件上传 #

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("uploads/" + 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),
    })
})

6.2 多文件上传 #

go
e.POST("/uploads", func(c echo.Context) error {
    form, err := c.MultipartForm()
    if err != nil {
        return err
    }
    
    files := form.File["files"]
    
    for _, file := range files {
        src, err := file.Open()
        if err != nil {
            return err
        }
        
        dst, err := os.Create("uploads/" + file.Filename)
        if err != nil {
            src.Close()
            return err
        }
        
        io.Copy(dst, src)
        
        src.Close()
        dst.Close()
    }
    
    return c.JSON(http.StatusOK, map[string]int{
        "count": len(files),
    })
})

6.3 文件信息 #

go
e.POST("/file-info", func(c echo.Context) error {
    file, err := c.FormFile("file")
    if err != nil {
        return err
    }
    
    return c.JSON(http.StatusOK, map[string]interface{}{
        "filename": file.Filename,
        "size":     file.Size,
        "header":   file.Header,
    })
})

七、数据绑定 #

7.1 Bind方法 #

go
type User struct {
    Name  string `json:"name" form:"name" query:"name" param:"name"`
    Email string `json:"email" form:"email" query:"email"`
    Age   int    `json:"age" form:"age" query:"age"`
}

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())
    }
    
    return c.JSON(http.StatusCreated, u)
})

7.2 绑定来源 #

go
type User struct {
    Name string `query:"name"`
}

e.GET("/users", func(c echo.Context) error {
    u := new(User)
    if err := c.Bind(u); err != nil {
        return err
    }
    
    return c.JSON(http.StatusOK, u)
})

7.3 绑定优先级 #

text
1. URL路径参数 (param)
2. 查询参数 (query)
3. 请求体 (json/form)

八、请求验证 #

8.1 使用validator #

go
type User struct {
    Name     string `json:"name" validate:"required"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=6"`
    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 echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }
    
    return c.JSON(http.StatusCreated, u)
})

8.2 验证标签 #

标签 说明
required 必填
email 邮箱格式
min 最小值
max 最大值
gte 大于等于
lte 小于等于
oneof 枚举值
url URL格式

九、客户端信息 #

9.1 IP地址 #

go
e.GET("/ip", func(c echo.Context) error {
    realIP := c.RealIP()
    remoteAddr := c.Request().RemoteAddr
    
    return c.JSON(http.StatusOK, map[string]string{
        "realIP":     realIP,
        "remoteAddr": remoteAddr,
    })
})

9.2 协议信息 #

go
e.GET("/protocol", func(c echo.Context) error {
    scheme := c.Scheme()
    isTLS := c.IsTLS()
    proto := c.Request().Proto
    
    return c.JSON(http.StatusOK, map[string]interface{}{
        "scheme": scheme,
        "isTLS":  isTLS,
        "proto":  proto,
    })
})

十、完整示例 #

go
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "github.com/go-playground/validator/v10"
    "github.com/labstack/echo/v4"
)

type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name" validate:"required"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=6"`
    Age      int    `json:"age" validate:"gte=0,lte=130"`
}

type QueryParams struct {
    Page    int    `query:"page"`
    Size    int    `query:"size"`
    Keyword string `query:"keyword"`
}

type CustomValidator struct {
    validator *validator.Validate
}

func (cv *CustomValidator) Validate(i interface{}) error {
    return cv.validator.Struct(i)
}

func main() {
    e := echo.New()
    e.Validator = &CustomValidator{validator: validator.New()}
    
    e.GET("/", home)
    e.GET("/users", listUsers)
    e.GET("/users/:id", getUser)
    e.POST("/users", createUser)
    e.PUT("/users/:id", updateUser)
    e.POST("/upload", uploadFile)
    e.GET("/info", getInfo)
    
    e.Logger.Fatal(e.Start(":8080"))
}

func home(c echo.Context) error {
    return c.String(http.StatusOK, "Welcome!")
}

func listUsers(c echo.Context) error {
    q := new(QueryParams)
    if err := c.Bind(q); err != nil {
        return err
    }
    
    if q.Page == 0 {
        q.Page = 1
    }
    if q.Size == 0 {
        q.Size = 10
    }
    
    return c.JSON(http.StatusOK, map[string]interface{}{
        "page":    q.Page,
        "size":    q.Size,
        "keyword": q.Keyword,
        "users": []map[string]string{
            {"id": "1", "name": "张三"},
            {"id": "2", "name": "李四"},
        },
    })
}

func getUser(c echo.Context) error {
    id := c.Param("id")
    
    if _, err := strconv.Atoi(id); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, "ID必须是数字")
    }
    
    return c.JSON(http.StatusOK, map[string]string{
        "id":   id,
        "name": "用户" + id,
    })
}

func createUser(c echo.Context) error {
    u := new(User)
    if err := c.Bind(u); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }
    
    if err := c.Validate(u); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }
    
    u.ID = 1
    
    return c.JSON(http.StatusCreated, u)
}

func updateUser(c echo.Context) error {
    id := c.Param("id")
    
    u := new(User)
    if err := c.Bind(u); err != nil {
        return err
    }
    
    if err := c.Validate(u); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }
    
    u.ID, _ = strconv.Atoi(id)
    
    return c.JSON(http.StatusOK, u)
}

func uploadFile(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()
    
    os.MkdirAll("uploads", 0755)
    
    dst, err := os.Create("uploads/" + 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),
        "message":  "上传成功",
    })
}

func getInfo(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]interface{}{
        "method":    c.Request().Method,
        "path":      c.Path(),
        "realIP":    c.RealIP(),
        "scheme":    c.Scheme(),
        "isTLS":     c.IsTLS(),
        "userAgent": c.Request().UserAgent(),
        "headers":   c.Request().Header,
    })
}

十一、总结 #

请求处理方法:

方法 用途
Param() 路径参数
QueryParam() 查询参数
QueryParams() 所有查询参数
FormValue() 表单参数
FormFile() 上传文件
MultipartForm() 多文件表单
Bind() 数据绑定
Validate() 数据验证
Request() 请求对象
RealIP() 客户端IP

准备好学习响应处理了吗?让我们进入下一章!

最后更新:2026-03-28