EchoJuniorCoding

Как возвращать JSON, XML и другие форматы ответа в Echo?

Echo предоставляет c.JSON(), c.XML(), c.String(), c.HTML(), c.File(), c.Blob() и другие методы для отправки ответов разных форматов с нужным Content-Type.

Форматы ответов в Echo

Echo предоставляет набор методов на объекте echo.Context для отправки ответов в разных форматах. Каждый метод устанавливает правильный заголовок Content-Type и сериализует данные.

JSON

type User struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

e.GET("/user", func(c echo.Context) error {
	user := User{ID: 1, Name: "Alice"}
	// Content-Type: application/json; charset=UTF-8
	return c.JSON(http.StatusOK, user)
})

// Красивое форматирование (отступы)
e.GET("/user/pretty", func(c echo.Context) error {
	user := User{ID: 1, Name: "Alice"}
	return c.JSONPretty(http.StatusOK, user, "  ")
})

// JSONP для кросс-доменных запросов
e.GET("/user/jsonp", func(c echo.Context) error {
	user := User{ID: 1, Name: "Alice"}
	callback := c.QueryParam("callback")
	return c.JSONP(http.StatusOK, callback, user)
})

XML

type Product struct {
	XMLName xml.Name `xml:"product"`
	ID      int      `xml:"id"`
	Title   string   `xml:"title"`
}

e.GET("/product", func(c echo.Context) error {
	p := Product{ID: 42, Title: "Laptop"}
	// Content-Type: application/xml; charset=UTF-8
	return c.XML(http.StatusOK, p)
})

// С отступами
e.GET("/product/pretty", func(c echo.Context) error {
	p := Product{ID: 42, Title: "Laptop"}
	return c.XMLPretty(http.StatusOK, p, "  ")
})

Текст и HTML

// Обычный текст — Content-Type: text/plain; charset=UTF-8
e.GET("/ping", func(c echo.Context) error {
	return c.String(http.StatusOK, "pong")
})

// HTML — Content-Type: text/html; charset=UTF-8
e.GET("/hello", func(c echo.Context) error {
	return c.HTML(http.StatusOK, "<h1>Hello, World!</h1>")
})

Файлы и потоки

// Отдать файл с диска
e.GET("/download", func(c echo.Context) error {
	return c.File("./files/report.pdf")
})

// Файл как вложение (с заголовком Content-Disposition: attachment)
e.GET("/attach", func(c echo.Context) error {
	return c.Attachment("./files/report.pdf", "report.pdf")
})

// Файл для открытия в браузере
e.GET("/inline", func(c echo.Context) error {
	return c.Inline("./files/photo.jpg", "photo.jpg")
})

// Произвольный байтовый поток
e.GET("/blob", func(c echo.Context) error {
	data := []byte{0x89, 0x50, 0x4E, 0x47} // PNG header
	return c.Blob(http.StatusOK, "image/png", data)
})

// Стриминг из io.Reader
e.GET("/stream", func(c echo.Context) error {
	reader, _ := os.Open("./large-file.csv")
	defer reader.Close()
	return c.Stream(http.StatusOK, "text/csv", reader)
})

Согласование формата (Content Negotiation)

e.GET("/data", func(c echo.Context) error {
	data := map[string]string{"key": "value"}
	accept := c.Request().Header.Get("Accept")
	switch {
	case strings.Contains(accept, "application/xml"):
		return c.XML(http.StatusOK, data)
	default:
		return c.JSON(http.StatusOK, data)
	}
})

Подводные камни

  • Двойная запись: нельзя вызывать два метода ответа подряд (например, c.JSON() а потом c.String()) — заголовки и тело уже отправлены после первого вызова.
  • Возврат nil после записи: если вы вручную пишете в c.Response().Writer, а потом возвращаете nil, Echo не будет отправлять дополнительного ответа — убедитесь, что не вернули ошибку случайно.
  • Сериализация nil-слайсов: в Go nil-слайс сериализуется как null, а пустой []T{} — как []. Используйте make([]T, 0) для пустых массивов в JSON.
  • c.File() и path traversal: передача пользовательского ввода в c.File() без санации пути опасна — используйте filepath.Clean() и проверяйте, что результат находится внутри ожидаемой директории.
  • Большие JSON-ответы: c.JSON() сериализует всё в память перед отправкой — для больших данных используйте c.Stream() с json.NewEncoder().
  • Charset в Content-Type: Echo добавляет ; charset=UTF-8 к JSON и XML автоматически — не переопределяйте заголовок вручную без необходимости.

Common mistakes

  • Давать ответ про форматы ответов в Echo только на уровне определения, не показывая поведение в реальном приложении.
  • Игнорировать границы ответственности вокруг темы «форматы ответов в Echo»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
  • Не связывать форматы ответов в Echo с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.

What the interviewer is testing

  • Точно объясняет, что именно делает форматы ответов в Echo и где это используется в Go-коде.
  • Связывает форматы ответов в Echo с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
  • Не изобретает API и опирается на реальные контракты официальной документации.

Sources

Related topics