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

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

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

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

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

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

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

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

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

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

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

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

Все подборки
  • Python17 126
  • Облачные технологии14 083
  • QA вакансии3 456
  • Backend вакансии2 115
  • Инженер данных1 809
  • Продакт-менеджер1 261

Разработка

  • Android1 862
  • iOS1 700
  • Fullstack вакансии986
  • AI вакансии950
  • Frontend вакансии744
  • Mobile вакансии44

По стеку

  • Go8 688
  • Java6 537
  • Node.js2 026
  • .NET1 722
  • Kotlin1 611
  • Rust1 458
  • C#710
  • PHP682
  • C++571

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

  • Kubernetes7 772
  • DevOps вакансии6 054
  • Linux-инфраструктура5 493
  • Terraform / IaC4 275
  • CI/CD3 239
  • Мониторинг и Observability1 790
  • SRE568
  • Кибербезопасность458

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

  • Data Scientist1 143
  • Бизнес-аналитик923
  • Аналитик данных857
  • Системный аналитик152

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

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

По регионам

  • Вакансии в США44 276
  • Вакансии в Европе23 819
  • Вакансии в Канаде4 471
  • Работа в России3 904

По формату

  • Удалённые вакансии74 928
  • Релокация в США44 273
  • Гибридная работа12 388
  • Офисная работа7 083

По уровню

  • Senior вакансии42 625
  • Middle вакансии20 854
  • Junior вакансии6 721

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

Все компании
  • Wise190 вакансий
  • Rakuten189 вакансий
  • ASML166 вакансий
  • Grab137 вакансий
  • Celonis39 вакансий
  • Spotify37 вакансий
  • Adyen28 вакансий
  • Infobip28 вакансий
  • Monzo20 вакансий
  • Darktrace16 вакансий

Вакансии из 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