baseapp

package module
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 17 Imported by: 0

README

go-baseapp

BaseHandler, BaseService, BaseRepository, IBaseModel, IBaseRequest generic CRUD patterngo-baseapp

Documentation

Overview

Package baseapp provides a reusable base application framework for Go microservices built with Gin and GORM.

It extracts the common CRUD, pagination, soft-delete, and audit-logging patterns from real-world services into a generic, framework-agnostic library that can be consumed by any Go service.

Quick Start

// Define your model:
type Product struct {
    baseapp.BaseModel
    Name  string `json:"name"`
    Price int    `json:"price"`
}

// Define your request DTO:
type ProductRequest struct {
    Name  string `json:"name" validate:"required"`
    Price int    `json:"price" validate:"gt=0"`
}

// Create repository:
repo := baseapp.NewBaseRepository[Product](db)

// Create service:
svc := baseapp.NewBaseService[Product, ProductRequest](repo)

// Create handler:
h := baseapp.NewBaseHandler[Product, ProductRequest](svc)

// Register routes:
r.GET("/products", h.List)
r.POST("/products", h.Create)
r.GET("/products/:id", h.Retrieve)
r.PUT("/products/:id", h.Update)
r.DELETE("/products/:id", h.Delete)
r.PUT("/products/:id/restore", h.Restore)

Design

  • Generic: uses Go type parameters for model and request types.
  • GORM-backed: repository uses gorm.io/gorm for database operations.
  • Gin-compatible: handler works with github.com/gin-gonic/gin.
  • Pluggable: auth context, audit logging, and validation are interfaces.
  • Soft-delete: built-in support for soft-delete and restore.
  • Pagination: built-in pagination, search, filter, and sort.

Index

Constants

View Source
const DefaultConnMaxIdleTime = 5 * time.Minute

DefaultConnMaxIdleTime is the default max idle time for a connection.

View Source
const DefaultConnMaxLifetime = 30 * time.Minute

DefaultConnMaxLifetime is the default max lifetime for a connection.

View Source
const DefaultMinIdleConns = 20

DefaultMinIdleConns is the default minimum idle connections in the pool.

View Source
const DefaultPoolSize = 100

DefaultPoolSize is the default Redis connection pool size for cache-heavy services.

Variables

View Source
var ErrDuplicate = errors.New("duplicate record")

ErrDuplicate is returned when a duplicate check fails.

View Source
var ErrNotFound = errors.New("record not found")

ErrNotFound is returned when a record is not found.

Functions

func GetValue added in v0.2.2

func GetValue(instance interface{}, field string) (interface{}, error)

GetValue returns the value of a struct field by name using reflection.

func IsValidColumnName added in v0.2.0

func IsValidColumnName(column string) bool

IsValidColumnName validates that a column name only contains safe characters (alphanumeric, underscore, and optional dot for table.column format).

func SanitizeOrderBy added in v0.2.0

func SanitizeOrderBy(column string) string

SanitizeOrderBy validates an order by column name, returning empty string if invalid.

func SendBadRequest

func SendBadRequest(c *gin.Context, message string)

SendBadRequest sends a 400 Bad Request response.

func SendCreated

func SendCreated(c *gin.Context, data any)

SendCreated sends a 201 Created response with data.

func SendError

func SendError(c *gin.Context, code int, message string)

SendError sends a custom error response with a specified status code.

func SendForbidden

func SendForbidden(c *gin.Context, message string)

SendForbidden sends a 403 Forbidden response.

func SendInternalServerError

func SendInternalServerError(c *gin.Context, err error)

SendInternalServerError sends a 500 Internal Server Error response.

func SendNotFound

func SendNotFound(c *gin.Context)

SendNotFound sends a 404 Not Found response.

func SendSuccess

func SendSuccess(c *gin.Context)

SendSuccess sends a standard success response.

func SendSuccessWithData

func SendSuccessWithData(c *gin.Context, data any)

SendSuccessWithData sends a success response with data.

func SendUnauthorized

func SendUnauthorized(c *gin.Context, message string)

SendUnauthorized sends a 401 Unauthorized response.

func SetValue added in v0.2.2

func SetValue(instance interface{}, field string, value interface{}) error

SetValue sets the value of a struct field by name using reflection.

func ValidateSortDirection added in v0.2.0

func ValidateSortDirection(sort string) string

ValidateSortDirection ensures sort is either "asc" or "desc".

Types

type AuditLogger

type AuditLogger interface {
	LogCreate(ctx interface{}, model IModel, auth AuthContext) error
	LogUpdate(ctx interface{}, model IModel, prevData []byte, auth AuthContext) error
	LogDelete(ctx interface{}, model IModel, auth AuthContext) error
}

AuditLogger logs audit events for create/update/delete operations. Implement this to record changes to an audit table or external system.

type AuthContext

type AuthContext interface {
	// UserID returns the authenticated user's UUID.
	UserID() uuid.UUID
	// UserName returns the authenticated user's display name.
	UserName() string
}

AuthContext provides the authenticated user context for a request. Implement this interface in your service to provide user info to the base handler/service layer.

type AuthContextProvider

type AuthContextProvider interface {
	// FromGinContext extracts the auth context from a Gin context.
	// Returns an error if the user is not authenticated.
	FromGinContext(c interface{}) (AuthContext, error)
}

AuthContextProvider extracts an AuthContext from a Gin context. Implement this to bridge your auth middleware with baseapp.

type BaseHandler

type BaseHandler[T any, R any] struct {
	Service    IBaseService[T, R]
	MaxPerPage int
}

BaseHandler provides generic CRUD HTTP handlers for Gin.

Usage:

h := baseapp.NewBaseHandler[Product, ProductRequest](svc)
r.GET("/products", h.List)
r.POST("/products", h.Create)

func NewBaseHandler

func NewBaseHandler[T any, R any](svc IBaseService[T, R]) *BaseHandler[T, R]

NewBaseHandler creates a new BaseHandler.

func (*BaseHandler[T, R]) Create

func (h *BaseHandler[T, R]) Create(c *gin.Context)

Create handles POST requests.

func (*BaseHandler[T, R]) Delete

func (h *BaseHandler[T, R]) Delete(c *gin.Context)

Delete handles DELETE /:id requests (soft-delete).

func (*BaseHandler[T, R]) List

func (h *BaseHandler[T, R]) List(c *gin.Context)

List handles GET requests with pagination, search, and filters.

func (*BaseHandler[T, R]) Restore

func (h *BaseHandler[T, R]) Restore(c *gin.Context)

Restore handles PUT /:id/restore requests.

func (*BaseHandler[T, R]) Retrieve

func (h *BaseHandler[T, R]) Retrieve(c *gin.Context)

Retrieve handles GET /:id requests.

func (*BaseHandler[T, R]) Update

func (h *BaseHandler[T, R]) Update(c *gin.Context)

Update handles PUT /:id requests.

type BaseModel

type BaseModel struct {
	ID   uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4();primaryKey" json:"id"`
	Code string    `gorm:"type:varchar(255);unique;default:uuid_generate_v4();" json:"code"`

	// References id for draft
	TRefId *uuid.UUID `gorm:"index;type:uuid" json:"refId"`

	// Approval
	Status DataStatus `gorm:"type:varchar(50);default:PUBLISHED;" json:"status"`

	CreatorId   *uuid.UUID      `gorm:"type:uuid" json:"-"`
	CreatorName *string         `gorm:"type:varchar(255)" json:"creatorName"`
	UpdaterId   *uuid.UUID      `gorm:"type:uuid" json:"-"`
	UpdaterName *string         `gorm:"type:varchar(255)" json:"updaterName"`
	DeleterId   *uuid.UUID      `gorm:"type:uuid" json:"-"`
	DeleterName *string         `gorm:"type:varchar(255)" json:"deleterName"`
	CreatedAt   time.Time       `gorm:"type:timestamp" json:"createdAt"`
	UpdatedAt   *time.Time      `gorm:"type:timestamp" json:"updatedAt"`
	DeletedAt   *gorm.DeletedAt `gorm:"index" json:"deletedAt"`
	ApprovedAt  *time.Time      `gorm:"type:timestamp" json:"approvedAt"`
}

BaseModel provides the common fields for all models. Service-specific BaseModel wrappers should embed this struct and add service-specific associations (e.g., Creator/Updater/Deleter foreign-key pointers).

func (*BaseModel) BeforeCreate

func (b *BaseModel) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets a UUID if not already set. GORM hook.

func (*BaseModel) GetCode added in v0.2.2

func (b *BaseModel) GetCode() string

GetCode returns the model code.

func (*BaseModel) GetDeletedAt added in v0.2.2

func (b *BaseModel) GetDeletedAt() *gorm.DeletedAt

GetDeletedAt returns the deleted timestamp.

func (*BaseModel) GetID

func (b *BaseModel) GetID() uuid.UUID

GetID returns the model's UUID.

func (*BaseModel) GetRefId added in v0.2.2

func (b *BaseModel) GetRefId() *uuid.UUID

GetRefId returns the draft reference ID.

func (*BaseModel) GetStatus added in v0.2.2

func (b *BaseModel) GetStatus() DataStatus

GetStatus returns the model status.

func (*BaseModel) SetApprovedAt added in v0.2.2

func (b *BaseModel) SetApprovedAt(value *time.Time)

SetApprovedAt sets the approved-at timestamp.

func (*BaseModel) SetCreator

func (b *BaseModel) SetCreator(id uuid.UUID, name string)

SetCreator sets the creator audit fields.

func (*BaseModel) SetCreatorId added in v0.2.2

func (b *BaseModel) SetCreatorId(value *uuid.UUID)

SetCreatorId sets the creator UUID.

func (*BaseModel) SetCreatorName added in v0.2.2

func (b *BaseModel) SetCreatorName(value *string)

SetCreatorName sets the creator name.

func (*BaseModel) SetDeletedAt added in v0.2.2

func (b *BaseModel) SetDeletedAt(value *gorm.DeletedAt)

SetDeletedAt sets the deleted-at timestamp.

func (*BaseModel) SetDeleterId added in v0.2.2

func (b *BaseModel) SetDeleterId(value *uuid.UUID)

SetDeleterId sets the deleter UUID.

func (*BaseModel) SetDeleterName added in v0.2.2

func (b *BaseModel) SetDeleterName(value *string)

SetDeleterName sets the deleter name.

func (*BaseModel) SetID added in v0.2.2

func (b *BaseModel) SetID(value uuid.UUID)

SetID sets the model UUID.

func (*BaseModel) SetStatus added in v0.2.2

func (b *BaseModel) SetStatus(value DataStatus)

SetStatus sets the model status.

func (*BaseModel) SetUpdater

func (b *BaseModel) SetUpdater(id uuid.UUID, name string)

SetUpdater sets the updater audit fields.

func (*BaseModel) SetUpdaterId added in v0.2.2

func (b *BaseModel) SetUpdaterId(value *uuid.UUID)

SetUpdaterId sets the updater UUID.

func (*BaseModel) SetUpdaterName added in v0.2.2

func (b *BaseModel) SetUpdaterName(value *string)

SetUpdaterName sets the updater name.

type BaseRepository

type BaseRepository[T any] struct {
	DB *gorm.DB
}

BaseRepository provides generic CRUD operations using GORM.

Usage:

repo := baseapp.NewBaseRepository[Product](db)
products, err := repo.GetAll(opts)

func NewBaseRepository

func NewBaseRepository[T any](db *gorm.DB) *BaseRepository[T]

NewBaseRepository creates a new BaseRepository for the given model type.

func (*BaseRepository[T]) Create

func (r *BaseRepository[T]) Create(model *T, tx *gorm.DB) (*T, error)

Create inserts a new record. If tx is nil, it runs in an implicit transaction.

func (*BaseRepository[T]) GetAll

func (r *BaseRepository[T]) GetAll(opts QueryOptions) (*ListResult[T], error)

GetAll returns a paginated, filtered, sorted list of records.

func (*BaseRepository[T]) GetByID

func (r *BaseRepository[T]) GetByID(id uuid.UUID, associations []string) (*T, error)

GetByID retrieves a single record by ID with optional associations (preloads).

func (*BaseRepository[T]) GetDB

func (r *BaseRepository[T]) GetDB() *gorm.DB

GetDB returns the underlying GORM DB instance.

func (*BaseRepository[T]) GetDeletedByID

func (r *BaseRepository[T]) GetDeletedByID(id uuid.UUID) (*T, error)

GetDeletedByID retrieves a soft-deleted record by ID.

func (*BaseRepository[T]) Restore

func (r *BaseRepository[T]) Restore(id uuid.UUID, tx *gorm.DB) error

Restore un-deletes a soft-deleted record by ID. If tx is nil, it uses the default DB.

func (*BaseRepository[T]) SoftDelete

func (r *BaseRepository[T]) SoftDelete(id uuid.UUID, tx *gorm.DB) error

SoftDelete soft-deletes a record by ID. If tx is nil, it uses the default DB.

func (*BaseRepository[T]) Update

func (r *BaseRepository[T]) Update(model *T, tx *gorm.DB) (*T, error)

Update saves changes to an existing record. If tx is nil, it runs in an implicit transaction.

type BaseService

type BaseService[T any, R any] struct {
	Repository   IBaseRepository[T]
	AuthProvider AuthContextProvider
	Validator    Validator[R]
	DupChecker   DuplicateChecker[R]
	AuditLogger  AuditLogger
}

BaseService provides the service layer with optional validation, duplicate checking, and audit logging.

Usage:

svc := baseapp.NewBaseService[Product, ProductRequest](repo)
// With optional features:
svc.WithValidator(myValidator)
svc.WithDuplicateChecker(myDupChecker)
svc.WithAuditLogger(myAuditLogger)

func NewBaseService

func NewBaseService[T any, R any](repo IBaseRepository[T]) *BaseService[T, R]

NewBaseService creates a new BaseService for the given model and request types.

func (*BaseService[T, R]) Create

func (s *BaseService[T, R]) Create(ctx interface{}, req *R) (*T, error)

Create validates the request, checks for duplicates, creates the record, and logs the audit event.

func (*BaseService[T, R]) GetAll

func (s *BaseService[T, R]) GetAll(ctx interface{}, opts QueryOptions) (*ListResult[T], error)

GetAll returns a paginated list of records.

func (*BaseService[T, R]) GetByID

func (s *BaseService[T, R]) GetByID(id uuid.UUID, associations []string) (*T, error)

GetByID retrieves a single record by ID.

func (*BaseService[T, R]) Restore

func (s *BaseService[T, R]) Restore(ctx interface{}, id uuid.UUID) error

Restore un-deletes a soft-deleted record.

func (*BaseService[T, R]) SoftDelete

func (s *BaseService[T, R]) SoftDelete(ctx interface{}, id uuid.UUID) error

SoftDelete soft-deletes a record and logs the audit event.

func (*BaseService[T, R]) Update

func (s *BaseService[T, R]) Update(ctx interface{}, id uuid.UUID, req *R) (*T, error)

Update validates the request, checks for duplicates, updates the record, and logs the audit event.

func (*BaseService[T, R]) WithAuditLogger

func (s *BaseService[T, R]) WithAuditLogger(l AuditLogger) *BaseService[T, R]

WithAuditLogger sets the audit logger.

func (*BaseService[T, R]) WithAuthProvider

func (s *BaseService[T, R]) WithAuthProvider(p AuthContextProvider) *BaseService[T, R]

WithAuthProvider sets the auth context provider.

func (*BaseService[T, R]) WithDuplicateChecker

func (s *BaseService[T, R]) WithDuplicateChecker(d DuplicateChecker[R]) *BaseService[T, R]

WithDuplicateChecker sets the duplicate checker.

func (*BaseService[T, R]) WithValidator

func (s *BaseService[T, R]) WithValidator(v Validator[R]) *BaseService[T, R]

WithValidator sets the request validator.

type DataStatus added in v0.2.2

type DataStatus string

DataStatus represents the lifecycle state of a model record.

const (
	DataStatusPublished        DataStatus = "PUBLISHED"         // displayed for all
	DataStatusWaitingApproval  DataStatus = "WAITING_APPROVAL"  // displayed to approver and creator
	DataStatusDraft            DataStatus = "DRAFT"             // displayed to creator org only
	DataStatusApprovalRequired DataStatus = "APPROVAL_REQUIRED" // displayed to creator with submit approval button
	DataStatusApproved         DataStatus = "APPROVED"          // displayed for all
	DataStatusApprovedPartialy DataStatus = "APPROVED_PARTIALY" // displayed to approver and creator
	DataStatusRejected         DataStatus = "REJECTED"          // displayed to approver and creator
)

func (DataStatus) IsValid added in v0.2.2

func (o DataStatus) IsValid() bool

func (*DataStatus) Scan added in v0.2.2

func (o *DataStatus) Scan(value interface{}) error

func (DataStatus) Value added in v0.2.2

func (o DataStatus) Value() (driver.Value, error)

type DuplicateChecker

type DuplicateChecker[R any] interface {
	CheckDuplicate(req *R, existingID *uuid.UUID) error
}

DuplicateChecker checks for duplicate values before create/update. Return nil if no duplicate, or an error describing the conflict.

type IBaseModel added in v0.2.2

type IBaseModel interface {
	// getters
	GetID() uuid.UUID
	GetCode() string
	GetStatus() DataStatus
	GetDeletedAt() *gorm.DeletedAt
	GetRefId() *uuid.UUID

	// setters
	SetID(uuid.UUID)
	SetCreatorId(*uuid.UUID)
	SetCreatorName(*string)
	SetUpdaterId(*uuid.UUID)
	SetUpdaterName(*string)
	SetDeleterId(*uuid.UUID)
	SetDeleterName(*string)
	SetDeletedAt(*gorm.DeletedAt)
	SetApprovedAt(*time.Time)
	SetStatus(DataStatus)
}

IBaseModel is the interface implemented by all service-specific base models.

type IBaseRepository

type IBaseRepository[T any] interface {
	GetDB() *gorm.DB
	GetAll(opts QueryOptions) (*ListResult[T], error)
	GetByID(id uuid.UUID, associations []string) (*T, error)
	GetDeletedByID(id uuid.UUID) (*T, error)
	Create(model *T, tx *gorm.DB) (*T, error)
	Update(model *T, tx *gorm.DB) (*T, error)
	SoftDelete(id uuid.UUID, tx *gorm.DB) error
	Restore(id uuid.UUID, tx *gorm.DB) error
}

IBaseRepository is the interface for generic CRUD operations.

type IBaseService

type IBaseService[T any, R any] interface {
	GetAll(ctx interface{}, opts QueryOptions) (*ListResult[T], error)
	GetByID(id uuid.UUID, associations []string) (*T, error)
	Create(ctx interface{}, req *R) (*T, error)
	Update(ctx interface{}, id uuid.UUID, req *R) (*T, error)
	SoftDelete(ctx interface{}, id uuid.UUID) error
	Restore(ctx interface{}, id uuid.UUID) error
}

IBaseService is the interface for the service layer.

type IModel

type IModel interface {
	TableName() string
	GetID() uuid.UUID
}

IModel is the interface that all models must implement. Embed BaseModel to satisfy this interface automatically.

type ListResult

type ListResult[T any] struct {
	Data              []T   `json:"data"`
	TotalData         int64 `json:"total_data"`
	TotalFilteredData int64 `json:"total_filtered_data"`
	Page              int   `json:"page"`
	PerPage           int   `json:"per_page"`
	TotalPages        int   `json:"total_pages"`
}

ListResult is the paginated result returned by GetAll.

type QueryOptions

type QueryOptions struct {
	Page          int
	PerPage       int
	Search        string
	SearchColumns []string
	OrderBy       []string
	Sort          string
	IsDeleted     bool
	Filters       map[string]string
}

QueryOptions controls list queries: pagination, search, filter, sort.

func DefaultQueryOptions

func DefaultQueryOptions() QueryOptions

DefaultQueryOptions returns sensible defaults for a list query.

func (QueryOptions) Limit

func (o QueryOptions) Limit(maxPerPage int) int

Limit returns the SQL LIMIT value, capped at maxPerPage.

func (QueryOptions) Offset

func (o QueryOptions) Offset() int

Offset returns the SQL OFFSET value for pagination.

type RedisClient

type RedisClient struct {
	Client  *redis.Client
	Cluster *redis.ClusterClient
	// contains filtered or unexported fields
}

RedisClient wraps github.com/redis/go-redis/v9.Client with common helper methods for caching, locking, and pub/sub patterns. It supports both single-node and cluster mode transparently.

Usage:

client := baseapp.NewRedisClient(&redis.Options{Addr: "<REDIS_HOST>:6379"})
defer client.Close()

// Set with TTL:
client.Set(ctx, "key", "value", 5*time.Minute)

// Get:
val, err := client.Get(ctx, "key")

func NewRedisClient

func NewRedisClient(opts *redis.Options) *RedisClient

NewRedisClient creates a new RedisClient from redis.Options.

func NewRedisClientFromClient

func NewRedisClientFromClient(client *redis.Client) *RedisClient

NewRedisClientFromClient wraps an existing redis.Client.

func NewRedisClientFromConfig added in v0.2.3

func NewRedisClientFromConfig(host, port, username, password string, db int) *RedisClient

NewRedisClientFromConfig creates a new RedisClient from raw connection parameters with sensible pool defaults. Returns nil if host is empty. If REDIS_CLUSTER_MODE=true, it reads REDIS_CLUSTER_ADDRS (comma-separated) and creates a cluster client instead of a single-node client.

func NewRedisClusterClient added in v0.2.3

func NewRedisClusterClient(addrs []string, username, password string) *RedisClient

NewRedisClusterClient creates a new RedisClient in cluster mode from the given addresses.

func (*RedisClient) AcquireLock

func (r *RedisClient) AcquireLock(ctx context.Context, key string, ttl time.Duration) (bool, error)

AcquireLock acquires a distributed lock with a TTL. Returns true if the lock was acquired, false if it was already held by another process. Use ReleaseLock to release the lock before the TTL expires.

func (*RedisClient) Close

func (r *RedisClient) Close() error

Close closes the Redis connection.

func (*RedisClient) Delete

func (r *RedisClient) Delete(ctx context.Context, keys ...string) error

Delete removes one or more keys.

func (*RedisClient) DeletePattern added in v0.2.3

func (r *RedisClient) DeletePattern(ctx context.Context, pattern string) error

DeletePattern deletes all keys matching the given glob pattern.

func (*RedisClient) Exists

func (r *RedisClient) Exists(ctx context.Context, key string) (bool, error)

Exists checks if a key exists.

func (*RedisClient) Expire

func (r *RedisClient) Expire(ctx context.Context, key string, ttl time.Duration) error

Expire sets a TTL on an existing key.

func (*RedisClient) Get

func (r *RedisClient) Get(ctx context.Context, key string) (string, error)

Get retrieves a string value by key. Returns empty string if key doesn't exist.

func (*RedisClient) GetBytes

func (r *RedisClient) GetBytes(ctx context.Context, key string) ([]byte, error)

GetBytes retrieves a value as bytes by key.

func (*RedisClient) GetJSON

func (r *RedisClient) GetJSON(ctx context.Context, key string, target any) error

GetJSON retrieves a JSON value and unmarshals it into the target.

func (*RedisClient) GetJSONFound added in v0.2.3

func (r *RedisClient) GetJSONFound(ctx context.Context, key string, target any) (bool, error)

GetJSONFound retrieves a JSON value and unmarshals it into the target. Returns (true, nil) if found, (false, nil) if not found.

func (*RedisClient) Incr

func (r *RedisClient) Incr(ctx context.Context, key string) (int64, error)

Incr increments a key by 1 and returns the new value.

func (*RedisClient) IncrBy

func (r *RedisClient) IncrBy(ctx context.Context, key string, delta int64) (int64, error)

IncrBy increments a key by delta and returns the new value.

func (*RedisClient) Ping

func (r *RedisClient) Ping(ctx context.Context) error

Ping checks the Redis connection.

func (*RedisClient) ReleaseLock

func (r *RedisClient) ReleaseLock(ctx context.Context, key string) error

ReleaseLock releases a distributed lock.

func (*RedisClient) Set

func (r *RedisClient) Set(ctx context.Context, key string, value any, ttl time.Duration) error

Set sets a key with a TTL. If ttl is 0, the key has no expiration.

func (*RedisClient) SetJSON

func (r *RedisClient) SetJSON(ctx context.Context, key string, value any, ttl time.Duration) error

SetJSON sets a key with a JSON-marshaled value and TTL.

func (*RedisClient) TTL

func (r *RedisClient) TTL(ctx context.Context, key string) (time.Duration, error)

TTL returns the remaining time-to-live for a key.

type Validator

type Validator[R any] interface {
	Validate(req *R) error
}

Validator validates a request before processing. Return nil if valid, or an error describing the validation failure.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL