Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

+666новых вакансий за сутки·52 265вакансий открыто сейчас·Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Новые вакансии в TelegramРаботодателям
Talanto
Вакансии
Компании5 686
РезюмеAIПодготовкаСтатьи
ТарифыТарифы
Войти

Не нашли подходящее?

Пусть нужная вакансия найдёт вас сама.

  1. Подборка в TelegramНовые вакансии по вашему фильтру — сразу в бота.Подключить
  2. Мэтчи по резюмеЗагрузите резюме — покажем, где вы подходите лучше всего.Загрузить
  3. Сохранить фильтрНастройте поиск один раз и возвращайтесь к новым вакансиям.Настроить
Что нового на TalantoTalanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Для работодателей

Опубликуйте вакансию — отклики придут в ваш кабинет.

  1. Вакансиябесплатно
  2. Откликиконтакты бесплатно
  3. Наймэтапы и интервью

40 компаний публикуют вакансии напрямую · 4 394 кандидата открыты к предложениям

Разместить вакансию

Популярные направления

Все подборки
  • Python5 973
  • Облачные технологии4 523
  • QA вакансии1 689
  • Android1 143
  • Инженер данных788
  • Продакт-менеджер308

Разработка

  • iOS1 093
  • Backend вакансии1 020
  • Fullstack вакансии448
  • AI вакансии378
  • Frontend вакансии369
  • Mobile вакансии27

По стеку

  • Go3 345
  • Java2 037
  • Kotlin908
  • Node.js746
  • Rust492
  • .NET481
  • PHP278
  • C++213
  • C#199

DevOps и инфраструктура

  • Kubernetes2 562
  • Linux-инфраструктура2 030
  • DevOps вакансии1 855
  • Terraform / IaC1 548
  • CI/CD876
  • Мониторинг и Observability709
  • SRE229
  • Кибербезопасность40

Аналитика и данные

  • Аналитик данных479
  • Data Scientist443
  • Бизнес-аналитик342
  • Системный аналитик50

Другие направления

  • Проджект-менеджер107
  • UI/UX дизайн70
  • Ручное тестирование14

По регионам

  • Вакансии в США24 039
  • Вакансии в Европе9 373
  • Работа в России2 968
  • Вакансии в Канаде2 114

По формату

  • Удалённые вакансии41 595
  • Релокация в США24 039
  • Гибридная работа5 209
  • Офисная работа3 834

По уровню

  • Senior вакансии22 591
  • Middle вакансии12 174
  • Junior вакансии4 013

Сейчас нанимают

Все компании
  • Rakuten189 вакансий
  • ASML163 вакансии
  • Grab134 вакансии
  • Wise81 вакансия
  • Infobip28 вакансий
  • Darktrace17 вакансий
  • Spotify17 вакансий
  • Celonis14 вакансий
  • UiPath13 вакансий
  • Adyen9 вакансий

Вакансии из 344 источников: карьерные страницы компаний и джоб-борды.

КомпанииО насНовостиПубличная офертаУсловия использованияПолитика конфиденциальностиПоддержка
←К теме GinВсе вопросы
GinJuniorКодинг

Как разбирать JSON-тело запроса в Gin с помощью c.ShouldBindJSON()?

В закладки
Сначала попробуйте ответить сами30 секунд
Сравнить с ответом ↓

c.ShouldBindJSON(&struct{}) десериализует JSON-тело запроса в Go-структуру с валидацией тегов binding и возвращает ошибку без автоматической отправки ответа. Предпочтительнее c.BindJSON(), который скрыто вызывает Abort при ошибке.

Базовый пример

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type CreateUserRequest struct {
	Name     string `json:"name"     binding:"required,min=2,max=100"`
	Email    string `json:"email"    binding:"required,email"`
	Age      int    `json:"age"      binding:"required,min=18,max=120"`
	Role     string `json:"role"     binding:"oneof=admin user viewer"`
}

func createUser(c *gin.Context) {
	var req CreateUserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// req.Name, req.Email, req.Age заполнены и валидны
	c.JSON(http.StatusCreated, gin.H{
		"id":    42,
		"name":  req.Name,
		"email": req.Email,
	})
}

func main() {
	r := gin.Default()
	r.POST("/users", createUser)
	r.Run(":8080")
}

Разбор JSON-тела запроса через c.ShouldBindJSON()

c.ShouldBindJSON() читает тело HTTP-запроса, десериализует JSON в переданную структуру и возвращает ошибку при неудаче — без автоматической отправки ответа клиенту. Это основной способ получать JSON-данные от клиента в Gin.

ShouldBindJSON vs BindJSON

Gin предоставляет два варианта:

  • c.ShouldBindJSON(&obj) — возвращает ошибку, не пишет ответ автоматически. Рекомендуется.
  • c.BindJSON(&obj) — при ошибке автоматически вызывает c.AbortWithStatus(400). Это скрывает логику и мешает отправить собственный JSON-ответ с деталями ошибки.
// НЕ делайте так — BindJSON уже отправил 400, ваш c.JSON не выполнится
if err := c.BindJSON(&req); err != nil {
	c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // лишний вызов
	return
}

// Правильно:
if err := c.ShouldBindJSON(&req); err != nil {
	c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	return
}

Кастомная обработка ошибок валидации

import (
	"errors"

	"github.com/go-playground/validator/v10"
)

func formatValidationErrors(err error) []gin.H {
	var ve validator.ValidationErrors
	if !errors.As(err, &ve) {
		return []gin.H{{"message": err.Error()}}
	}

	out := make([]gin.H, len(ve))
	for i, fe := range ve {
		out[i] = gin.H{
			"field":   fe.Field(),
			"tag":     fe.Tag(),
			"value":   fe.Param(),
			"message": fe.Translate(nil),
		}
	}
	return out
}

func createUser(c *gin.Context) {
	var req CreateUserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusUnprocessableEntity, gin.H{
			"errors": formatValidationErrors(err),
		})
		return
	}
	c.JSON(http.StatusCreated, gin.H{"name": req.Name})
}

Опциональные поля и указатели

type UpdateUserRequest struct {
	Name  *string `json:"name"  binding:"omitempty,min=2"`  // nil если не передано
	Email *string `json:"email" binding:"omitempty,email"`
}

func updateUser(c *gin.Context) {
	var req UpdateUserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if req.Name != nil {
		// обновить имя
	}
}

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

  • Тело запроса читается один раз. После ShouldBindJSON() c.Request.Body исчерпан. Повторный вызов вернёт ошибку EOF. Если нужно несколько биндингов, прочитайте тело в []byte через c.GetRawData() и восстановите через io.NopCloser.
  • Content-Type должен быть application/json. Если клиент не выставил заголовок, ShouldBindJSON игнорирует его — но декодирование может всё равно пройти. Явно проверяйте Content-Type при необходимости.
  • Поле с binding:"required" vs omitempty. Нулевое значение (0, "", false) удовлетворяет required только если поле присутствует в JSON. Используйте указатели для различения «не передано» и «передано с нулём».
  • Ошибка валидации vs ошибка декодирования. Невалидный JSON (синтаксическая ошибка) и ошибка валидации тега — разные типы. Первая — *json.SyntaxError, вторая — validator.ValidationErrors.
  • Глубокая вложенность и тег binding. Вложенные структуры должны иметь тег binding:"required" или dive для валидации элементов слайса — без них вложенные поля не валидируются.
  • Максимальный размер тела запроса. По умолчанию Gin не ограничивает размер Body. При загрузке больших JSON используйте http.MaxBytesReader(c.Writer, c.Request.Body, 1<<20) перед биндингом.

Частые вопросы на собеседовании

  • В каких backend-проектах Gin (Go framework) является сильным выбором, а где лучше выбрать более простой или другой стек?
  • Что такое система валидации Gin и как использовать struct tags для валидации входных данных?
  • Как писать unit-тесты для Gin-обработчиков с помощью httptest?
  • Как реализовать middleware аутентификации (JWT) в Gin?

Типичные ошибки

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

Что проверяет интервьюер

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

Как вы знаете этот вопрос?

Проверить себя по теме Gin →

Источники

  • https://gin-gonic.com/en/docs/binding/binding-and-validation/
  • https://pkg.go.dev/github.com/gin-gonic/gin
←Предыдущий вопросКак реализовать глобальную обработку ошибок в приложении на Gin?Следующий вопрос→Как goroutine взаимодействуют с `Context` Gin — почему не следует передавать `*gin.Context` в goroutine?

Связанные темы

Ginapi-designCodingGINrequest-validation