Ответ
Функция context.WithValue
используется для передачи метаданных, специфичных для запроса, по цепочке вызовов, особенно между горутинами. Это позволяет избежать "пробрасывания" таких данных через аргументы каждой функции.
Типичные примеры данных, передаваемых через контекст:
- ID запроса (request ID) для трассировки.
- Данные аутентификации пользователя.
- Информация для распределенной трассировки (tracing spans).
Лучшие практики и важные правила:
-
Не использовать для обязательных параметров. Контекст не должен использоваться для передачи обязательных аргументов функции. Такие параметры должны быть явными аргументами функции. Это делает API вашей функции понятным и самодостаточным.
- Плохо:
func DoSomething(ctx context.Context)
(где ID пользователя извлекается изctx
). - Хорошо:
func DoSomething(ctx context.Context, userID int)
.
- Плохо:
-
Использовать кастомные типы для ключей. Никогда не используйте встроенные типы (например,
string
) в качестве ключей дляcontext.WithValue
. Это может привести к коллизиям имен между разными пакетами. Создайте свой собственный неэкспортируемый тип.// Создаем неэкспортируемый тип для ключа type key int // Создаем экземпляр ключа const userIDKey key = 0 // Запись значения ctx = context.WithValue(ctx, userIDKey, 123) // Чтение значения userID, ok := ctx.Value(userIDKey).(int)
-
Контекст неизменяем (Immutable).
context.WithValue
возвращает новый объектContext
с добавленным значением, а не изменяет существующий. Всегда присваивайте результат обратно переменной контекста:ctx = context.WithValue(ctx, key, value)
. -
Храните только транзитные данные. Контекст предназначен для данных, жизненный цикл которых совпадает с жизненным циклом запроса. Не храните в нем ссылки на объекты с долгим временем жизни (например, пулы соединений с БД или логгеры).