← Назад к вопросам

Как пометить поле в gRPC, которое клиенту нельзя использовать?

2.0 Middle🔥 71 комментариев
#Микросервисы и архитектура#Сетевые протоколы и API

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как пометить поле в gRPC, которое клиенту нельзя использовать?

В экосистеме gRPC и Protocol Buffers (Protobuf) существует несколько подходов для пометки полей, которые не должны использоваться клиентом. Выбор конкретного метода зависит от того, хотим ли мы скрыть поле на уровне API, добавить семантические ограничения или реализовать проверки на стороне сервера.

Основные подходы:

1. Семантическое соглашение через комментарии

Самый простой способ — использовать соглашения в документации. Хотя это не предотвращает техническое использование поля, оно явно указывает на его предназначение.

message UserRequest {
  string username = 1;
  // @internal: Это поле используется только сервером, клиенты не должны его устанавливать.
  string internal_server_token = 2;
}

2. Использование зарезервированных полей (reserved)

Если нужно полностью удалить поле из публичного API и предотвратить его использование в будущем, можно поместить его в резерв.

message UserRequest {
  string username = 1;
  reserved 2; // Поле internal_server_token теперь зарезервировано
  reserved "internal_server_token";
}

Важно: этот подход удаляет поле из сгенерированного кода, поэтому он подходит только для этапов разработки, когда API ещё не стабилизировался.

3. Валидация на стороне сервера

Наиболее надёжный способ — обрабатывать и валидировать поля в реализации сервера. Даже если клиент отправит запрещённое поле, сервер может его игнорировать или возвращать ошибку.

// Пример на Go (используя пакет google.golang.org/grpc)
func (s *UserServer) CreateUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
    if req.InternalServerToken != "" {
        return nil, status.Error(codes.InvalidArgument, "internal_server_token не может быть установлен клиентом")
    }
    // Логика обработки...
}

4. Использование отдельных сообщений (разделение API)

Более чистая архитектурная практика — разделить сообщения для клиента и сервера.

// Публичное API для клиентов
message UserRequest {
  string username = 1;
}

// Внутреннее сообщение, используемое только сервером
message InternalUserRequest {
  string username = 1;
  string internal_server_token = 2;
}

На сервере можно конвертировать UserRequest в InternalUserRequest, добавляя необходимые внутренние поля.

5. Кастомные опции (custom options) в Protobuf

Можно создать кастомные опции для аннотирования полей, хотя это требует дополнительной настройки.

import "google/protobuf/descriptor.proto";

extend google.protobuf.FieldOptions {
    bool internal_only = 50000;
}

message UserRequest {
    string username = 1;
    string internal_server_token = 2 [(internal_only) = true];
}

Затем в коде генерации или в middleware можно проверять эти аннотации.

Рекомендации по использованию

Для большинства случаев я рекомендую комбинированный подход:

  1. Для публичного API используйте отдельные сообщения (п.4) — это наиболее чистое решение, которое явно разделяет контракты.
  2. Добавьте валидацию на сервере (п.3) как обязательный защитный слой.
  3. Используйте комментарии (п.1) для документирования ограничений.

Пример реализации на Go с разделением сообщений и валидацией:

// Обработчик gRPC
func (s *server) CreateUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
    // Конвертируем публичный запрос во внутренний
    internalReq := &internalpb.InternalUserRequest{
        Username: req.GetUsername(),
        // Сервер сам добавляет внутренний токен
        InternalToken: generateInternalToken(),
    }
    
    // Далее работаем только с internalReq
    return processUser(internalReq)
}

Важный нюанс: помните, что Protobuf не поддерживает "private fields" в традиционном ООП-смысле. Любое поле, объявленное в .proto файле, будет включено в сгенерированный код и потенциально доступно клиенту. Поэтому серверная валидация всегда необходима для безопасности.

Также стоит упомянуть инструменты вроде Buf Schema Registry, которые позволяют применять линтеры для проверки совместимости API и могут включать правила против использования определённых полей клиентами.

Как пометить поле в gRPC, которое клиенту нельзя использовать? | PrepBro