Ответ
Да, context.Context
позволяет передавать значения, но это его второстепенная функция. Основное назначение контекста — передача сигналов отмены, дедлайнов и тайм-аутов между горутинами в рамках одного запроса.
Для передачи данных используется функция context.WithValue
. Однако использовать ее нужно с осторожностью.
Ключевые правила и рекомендации:
Только для данных в рамках запроса (request-scoped). Идеальные кандидаты: ID запроса, токен аутентификации, информация о пользователе. Не используйте контекст для передачи опциональных параметров в функции.
Используйте кастомные типы для ключей. Чтобы избежать конфликтов ключей между разными пакетами, никогда не используйте встроенные типы вроде
string
илиint
в качестве ключей. Создайте свой неэкспортируемый тип.Значения должны быть потокобезопасными. Контекст может быть доступен из нескольких горутин одновременно.
Не замена DI или параметрам функции. Контекст не должен использоваться для передачи зависимостей (базы данных, логгеры) или основных аргументов функции. Это делает код менее явным и сложным для понимания.
Пример правильного использования:
// Создаем неэкспортируемый тип для ключа, чтобы избежать коллизий.
type key string
const userKey key = "user"
func main() {
// Создаем контекст с фонового и добавляем в него значение
ctx := context.WithValue(context.Background(), userKey, "Alice")
// ... передаем ctx дальше по цепочке вызовов ...
processRequest(ctx)
}
func processRequest(ctx context.Context) {
// Безопасно извлекаем значение
if user, ok := ctx.Value(userKey).(string); ok {
fmt.Printf("Processing request for user: %sn", user) // Processing request for user: Alice
} else {
fmt.Println("User not found in context")
}
}