响应处理 #
一、响应类型概述 #
Echo支持多种响应类型,满足不同的业务需求。
1.1 响应类型 #
| 方法 | 说明 | Content-Type |
|---|---|---|
| String | 字符串 | text/plain |
| JSON | JSON对象 | application/json |
| JSONP | JSONP | application/javascript |
| XML | XML | application/xml |
| HTML | HTML | text/html |
| File | 文件 | 根据文件类型 |
| Blob | 二进制 | 自定义 |
| Stream | 流 | 自定义 |
| NoContent | 无内容 | - |
二、字符串响应 #
2.1 基本用法 #
go
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
2.2 格式化字符串 #
go
e.GET("/user/:id", func(c echo.Context) error {
id := c.Param("id")
return c.String(http.StatusOK, fmt.Sprintf("User ID: %s", id))
})
2.3 多行文本 #
go
e.GET("/text", func(c echo.Context) error {
text := `第一行
第二行
第三行`
return c.String(http.StatusOK, text)
})
三、JSON响应 #
3.1 基本用法 #
go
e.GET("/json", func(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{
"message": "Hello",
"status": "success",
})
})
3.2 结构体响应 #
go
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
e.GET("/user", func(c echo.Context) error {
user := User{
ID: 1,
Name: "张三",
}
return c.JSON(http.StatusOK, user)
})
3.3 数组响应 #
go
e.GET("/users", func(c echo.Context) error {
users := []User{
{ID: 1, Name: "张三"},
{ID: 2, Name: "李四"},
{ID: 3, Name: "王五"},
}
return c.JSON(http.StatusOK, users)
})
3.4 嵌套结构 #
go
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Address Address `json:"address"`
}
e.GET("/user/detail", func(c echo.Context) error {
user := User{
ID: 1,
Name: "张三",
Address: Address{
City: "北京",
Country: "中国",
},
}
return c.JSON(http.StatusOK, user)
})
3.5 标准响应格式 #
go
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func Success(c echo.Context, data interface{}) error {
return c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
Data: data,
})
}
func Error(c echo.Context, code int, message string) error {
return c.JSON(code, Response{
Code: code,
Message: message,
})
}
e.GET("/success", func(c echo.Context) error {
return Success(c, map[string]string{"name": "张三"})
})
e.GET("/error", func(c echo.Context) error {
return Error(c, http.StatusBadRequest, "参数错误")
})
3.6 分页响应 #
go
type PageResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
Total int64 `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
func PageSuccess(c echo.Context, data interface{}, total int64, page, size int) error {
return c.JSON(http.StatusOK, PageResponse{
Code: 0,
Message: "success",
Data: data,
Total: total,
Page: page,
Size: size,
})
}
四、JSONP响应 #
4.1 基本用法 #
go
e.GET("/jsonp", func(c echo.Context) error {
callback := c.QueryParam("callback")
return c.JSONP(http.StatusOK, callback, map[string]string{
"message": "Hello",
})
})
4.2 测试 #
bash
curl "http://localhost:8080/jsonp?callback=myCallback"
myCallback({"message":"Hello"})
五、XML响应 #
5.1 基本用法 #
go
type User struct {
XMLName xml.Name `xml:"user"`
ID int `xml:"id"`
Name string `xml:"name"`
}
e.GET("/xml", func(c echo.Context) error {
user := User{
ID: 1,
Name: "张三",
}
return c.XML(http.StatusOK, user)
})
5.2 响应示例 #
xml
<user>
<id>1</id>
<name>张三</name>
</user>
六、HTML响应 #
6.1 基本用法 #
go
e.GET("/html", func(c echo.Context) error {
return c.HTML(http.StatusOK, "<h1>Hello, World!</h1>")
})
6.2 多行HTML #
go
e.GET("/page", func(c echo.Context) error {
html := `
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Welcome</h1>
<p>This is a paragraph.</p>
</body>
</html>
`
return c.HTML(http.StatusOK, html)
})
6.3 动态HTML #
go
e.GET("/user/:id", func(c echo.Context) error {
id := c.Param("id")
html := fmt.Sprintf(`
<html>
<body>
<h1>User Profile</h1>
<p>User ID: %s</p>
</body>
</html>
`, id)
return c.HTML(http.StatusOK, html)
})
七、文件响应 #
7.1 返回文件 #
go
e.GET("/file", func(c echo.Context) error {
return c.File("static/document.pdf")
})
7.2 文件下载 #
go
e.GET("/download", func(c echo.Context) error {
return c.Attachment("files/report.pdf", "report.pdf")
})
7.3 内联显示 #
go
e.GET("/inline", func(c echo.Context) error {
return c.Inline("files/document.pdf", "document.pdf")
})
7.4 动态生成文件 #
go
e.GET("/generate", func(c echo.Context) error {
content := "Hello, World!"
return c.Blob(http.StatusOK, "text/plain", []byte(content))
})
7.5 CSV下载 #
go
e.GET("/csv", func(c echo.Context) error {
var buf bytes.Buffer
writer := csv.NewWriter(&buf)
writer.Write([]string{"ID", "Name", "Email"})
writer.Write([]string{"1", "张三", "zhangsan@example.com"})
writer.Write([]string{"2", "李四", "lisi@example.com"})
writer.Flush()
c.Response().Header().Set("Content-Disposition", "attachment; filename=users.csv")
return c.Blob(http.StatusOK, "text/csv", buf.Bytes())
})
八、二进制响应 #
8.1 Blob响应 #
go
e.GET("/blob", func(c echo.Context) error {
data := []byte("Hello, World!")
return c.Blob(http.StatusOK, "text/plain", data)
})
8.2 图片响应 #
go
e.GET("/image", func(c echo.Context) error {
file, err := os.ReadFile("images/photo.jpg")
if err != nil {
return err
}
return c.Blob(http.StatusOK, "image/jpeg", file)
})
8.3 动态生成图片 #
go
e.GET("/qr", func(c echo.Context) error {
content := c.QueryParam("content")
img, err := generateQRCode(content)
if err != nil {
return err
}
var buf bytes.Buffer
png.Encode(&buf, img)
return c.Blob(http.StatusOK, "image/png", buf.Bytes())
})
九、流式响应 #
9.1 Stream响应 #
go
e.GET("/stream", func(c echo.Context) error {
reader := strings.NewReader("Hello, World!")
return c.Stream(http.StatusOK, "text/plain", reader)
})
9.2 大文件下载 #
go
e.GET("/large-file", func(c echo.Context) error {
file, err := os.Open("large-file.zip")
if err != nil {
return err
}
defer file.Close()
stat, _ := file.Stat()
c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size()))
c.Response().Header().Set("Content-Disposition", "attachment; filename=large-file.zip")
return c.Stream(http.StatusOK, "application/zip", file)
})
9.3 Server-Sent Events #
go
e.GET("/events", func(c echo.Context) error {
c.Response().Header().Set("Content-Type", "text/event-stream")
c.Response().Header().Set("Cache-Control", "no-cache")
c.Response().Header().Set("Connection", "keep-alive")
flusher, ok := c.Response().Writer.(http.Flusher)
if !ok {
return echo.NewHTTPError(http.StatusInternalServerError, "Streaming unsupported")
}
for i := 0; i < 10; i++ {
fmt.Fprintf(c.Response(), "data: Message %d\n\n", i)
flusher.Flush()
time.Sleep(1 * time.Second)
}
return nil
})
十、重定向 #
10.1 临时重定向 #
go
e.GET("/redirect", func(c echo.Context) error {
return c.Redirect(http.StatusFound, "/new-path")
})
10.2 永久重定向 #
go
e.GET("/old", func(c echo.Context) error {
return c.Redirect(http.StatusMovedPermanently, "/new")
})
10.3 外部URL #
go
e.GET("/external", func(c echo.Context) error {
return c.Redirect(http.StatusFound, "https://example.com")
})
十一、状态码 #
11.1 常用状态码 #
| 状态码 | 说明 |
|---|---|
| 200 | OK |
| 201 | Created |
| 204 | No Content |
| 301 | Moved Permanently |
| 302 | Found |
| 304 | Not Modified |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Internal Server Error |
11.2 无内容响应 #
go
e.DELETE("/users/:id", func(c echo.Context) error {
return c.NoContent(http.StatusNoContent)
})
11.3 自定义状态码 #
go
e.GET("/custom", func(c echo.Context) error {
return c.String(http.StatusCreated, "Created")
})
十二、响应头 #
12.1 设置响应头 #
go
e.GET("/headers", func(c echo.Context) error {
c.Response().Header().Set("X-Custom-Header", "value")
c.Response().Header().Set("X-Request-ID", "12345")
return c.String(http.StatusOK, "OK")
})
12.2 添加响应头 #
go
e.GET("/add-header", func(c echo.Context) error {
c.Response().Header().Add("Set-Cookie", "session=abc123")
c.Response().Header().Add("Set-Cookie", "token=xyz789")
return c.String(http.StatusOK, "OK")
})
12.3 缓存控制 #
go
e.GET("/cached", func(c echo.Context) error {
c.Response().Header().Set("Cache-Control", "max-age=3600")
c.Response().Header().Set("ETag", "abc123")
return c.String(http.StatusOK, "Cached content")
})
十三、完整示例 #
go
package main
import (
"bytes"
"encoding/csv"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/labstack/echo/v4"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func main() {
e := echo.New()
e.GET("/", home)
e.GET("/json", jsonExample)
e.GET("/xml", xmlExample)
e.GET("/html", htmlExample)
e.GET("/download", downloadFile)
e.GET("/csv", downloadCSV)
e.GET("/events", serverSentEvents)
e.GET("/redirect", redirectExample)
e.DELETE("/users/:id", deleteUser)
e.Logger.Fatal(e.Start(":8080"))
}
func home(c echo.Context) error {
return c.String(http.StatusOK, "Welcome!")
}
func jsonExample(c echo.Context) error {
return c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
Data: map[string]string{
"name": "张三",
"email": "zhangsan@example.com",
},
})
}
func xmlExample(c echo.Context) error {
type User struct {
XMLName xml.Name `xml:"user"`
Name string `xml:"name"`
Email string `xml:"email"`
}
user := User{
Name: "张三",
Email: "zhangsan@example.com",
}
return c.XML(http.StatusOK, user)
}
func htmlExample(c echo.Context) error {
html := `
<!DOCTYPE html>
<html>
<head><title>My Page</title></head>
<body><h1>Hello, World!</h1></body>
</html>
`
return c.HTML(http.StatusOK, html)
}
func downloadFile(c echo.Context) error {
return c.Attachment("files/report.pdf", "report.pdf")
}
func downloadCSV(c echo.Context) error {
var buf bytes.Buffer
writer := csv.NewWriter(&buf)
writer.Write([]string{"ID", "Name", "Email"})
writer.Write([]string{"1", "张三", "zhangsan@example.com"})
writer.Write([]string{"2", "李四", "lisi@example.com"})
writer.Flush()
c.Response().Header().Set("Content-Disposition", "attachment; filename=users.csv")
return c.Blob(http.StatusOK, "text/csv", buf.Bytes())
}
func serverSentEvents(c echo.Context) error {
c.Response().Header().Set("Content-Type", "text/event-stream")
c.Response().Header().Set("Cache-Control", "no-cache")
c.Response().Header().Set("Connection", "keep-alive")
flusher, ok := c.Response().Writer.(http.Flusher)
if !ok {
return echo.NewHTTPError(http.StatusInternalServerError, "Streaming unsupported")
}
for i := 0; i < 5; i++ {
fmt.Fprintf(c.Response(), "data: Message %d\n\n", i)
flusher.Flush()
time.Sleep(1 * time.Second)
}
return nil
}
func redirectExample(c echo.Context) error {
return c.Redirect(http.StatusFound, "/")
}
func deleteUser(c echo.Context) error {
id := c.Param("id")
fmt.Printf("Deleting user %s\n", id)
return c.NoContent(http.StatusNoContent)
}
十四、总结 #
响应方法要点:
| 方法 | 用途 | 示例 |
|---|---|---|
| String | 字符串 | c.String(200, "text") |
| JSON | JSON | c.JSON(200, data) |
| JSONP | JSONP | c.JSONP(200, "callback", data) |
| XML | XML | c.XML(200, data) |
| HTML | HTML | c.HTML(200, "<h1>...</h1>") |
| File | 文件 | c.File("path") |
| Attachment | 下载 | c.Attachment("path", "name") |
| Blob | 二进制 | c.Blob(200, "type", data) |
| Stream | 流 | c.Stream(200, "type", reader) |
| NoContent | 无内容 | c.NoContent(204) |
| Redirect | 重定向 | c.Redirect(302, "/path") |
准备好学习数据绑定与验证了吗?让我们进入下一章!
最后更新:2026-03-28