Protoc plugin to generate polyglot message validators for protocol buffers
—
The generated code API provides validation methods and utilities for each target language. The specific API varies by language but provides consistent validation functionality across all supported platforms.
Go code generation produces validation methods directly on protobuf message types with zero runtime dependencies.
func (m *Message) Validate() errorValidates the message according to its validation rules, returning the first error encountered.
Returns: error - First validation error, or nil if valid
Behavior:
Usage:
user := &User{Email: "invalid-email"}
if err := user.Validate(); err != nil {
log.Printf("Validation failed: %v", err)
}func (m *Message) ValidateAll() errorValidates the message and collects all validation errors into a single error.
Returns: error - Combined validation errors, or nil if valid
Behavior:
Usage:
user := &User{Email: "invalid", Age: -1}
if err := user.ValidateAll(); err != nil {
// err contains both email and age validation errors
log.Printf("All validation errors: %v", err)
}Generated Go code uses standard error interface with descriptive messages:
// Example error messages
"invalid User.Email: value must be a valid email address"
"invalid User.Age: value must be greater than 0"
"invalid User.Name: value length must be at least 1 characters"Generated validation integrates seamlessly with existing Go protobuf code:
import (
"github.com/example/proto/user"
// validation methods are automatically available
)
func processUser(u *user.User) error {
if err := u.Validate(); err != nil {
return fmt.Errorf("invalid user: %w", err)
}
// process valid user
return nil
}Java code generation produces validator classes with reflection-based validation and gRPC integration support.
interface Validator<T> {
void assertValid(T message) throws ValidationException;
}Base interface for all generated validators.
Type Parameters:
T: Message type being validatedMethods:
assertValid(T message): Validates message, throws on failureclass ReflectiveValidatorIndex {
public Validator validatorFor(Class<?> clazz);
}Main entry point for accessing validators by message class.
Methods:
validatorFor(Class<?> clazz): Returns validator for message classUsage:
ValidatorIndex index = new ReflectiveValidatorIndex();
Validator<User> userValidator = index.validatorFor(User.class);
userValidator.assertValid(user); // throws ValidationException if invalidclass ValidatingClientInterceptor implements ClientInterceptor
class ValidatingServerInterceptor implements ServerInterceptorgRPC interceptors for automatic request/response validation.
Usage:
// Client-side validation
clientStub = clientStub.withInterceptors(new ValidatingClientInterceptor(index));
// Server-side validation
serverBuilder.addService(
ServerInterceptors.intercept(service, new ValidatingServerInterceptor(index))
);class ValidationException extends Exception {
public ValidationException(String message);
public String getFieldPath();
public Object getInvalidValue();
}Exception thrown when validation fails.
Methods:
getFieldPath(): Returns field path that failed validationgetInvalidValue(): Returns the invalid valueC++ code generation produces validation functions with string-based error reporting.
bool Validate(const Message& msg, std::string* error = nullptr);Validates a message and optionally returns error details.
Parameters:
msg: Const reference to message to validateerror: Optional pointer to string for error detailsReturns: bool - true if valid, false if invalid
Usage:
User user;
user.set_email("invalid-email");
std::string error;
if (!Validate(user, &error)) {
std::cerr << "Validation failed: " << error << std::endl;
}Generated C++ code is organized in the validate namespace:
namespace validate {
bool Validate(const example::User& msg, std::string* error);
bool Validate(const example::Order& msg, std::string* error);
// ... other message validators
}C++ generation produces separate header and implementation files:
Header (.pb.validate.h):
Implementation (.pb.validate.cc):
Python uses runtime code generation with dynamic validation function creation.
def validate(msg) -> NoneValidates a protobuf message instance, raising exception on failure.
Parameters:
msg: Protobuf message instance to validateRaises: ValidationFailed - When validation fails
Usage:
from entities_pb2 import User
from protoc_gen_validate.validator import validate, ValidationFailed
user = User(email="invalid-email")
try:
validate(user)
except ValidationFailed as e:
print(f"Validation failed: {e}")def print_validate(msg) -> NonePrints the generated validation code for debugging purposes.
Parameters:
msg: Protobuf message instanceBehavior:
Usage:
from protoc_gen_validate.validator import print_validate
user = User()
print_validate(user) # Prints generated validation functionclass ValidationFailed(Exception):
"""Raised when message validation fails"""
passException raised when validation fails.
Inheritance: Standard Python Exception
Usage:
try:
validate(message)
except ValidationFailed as e:
logger.error(f"Invalid message: {e}")Python runtime includes performance optimizations:
exec()All generated code maintains consistent behavior across languages:
Go:
Java:
C++:
Python:
All generated code supports testing scenarios:
Install with Tessl CLI
npx tessl i tessl/go-protoc-gen-validate