Components¶
Components are independent functional modules in the framework. They implement the core.IService interface, are registered via Service(), and can be used by other components.
Creating a Component¶
Implement the core.IService interface:
package main
import (
"github.com/chuccp/go-web-frame/core"
)
type CacheComponent struct {
core.IService
cache map[string]string
}
func (c *CacheComponent) Init(ctx *core.Context) error {
c.cache = make(map[string]string)
return nil
}
func (c *CacheComponent) Get(key string) string {
return c.cache[key]
}
func (c *CacheComponent) Set(key, value string) {
c.cache[key] = value
}
Register a Component¶
Use in a Controller¶
type MyController struct {
core.IService
cache *CacheComponent
}
func (c *MyController) Init(ctx *core.Context) error {
c.cache = wf.GetService[*CacheComponent](ctx)
ctx.Get("/cache", c.Handle)
return nil
}
Built-in Components¶
Rate Limiting (ratelimit)¶
Token bucket-based rate limiting component:
package main
import (
"errors"
wf "github.com/chuccp/go-web-frame"
"github.com/chuccp/go-web-frame/component/ratelimit"
"github.com/chuccp/go-web-frame/config"
"github.com/chuccp/go-web-frame/core"
"github.com/chuccp/go-web-frame/web"
)
type MyController struct {
core.IService
rateLimit *ratelimit.RateLimit
}
func (c *MyController) Init(ctx *core.Context) error {
c.rateLimit = wf.GetService[*ratelimit.RateLimit](ctx)
ctx.Get("/api/data", c.Handle)
return nil
}
func (c *MyController) Handle(req *web.Request) (any, error) {
// Check rate limit
if !c.rateLimit.AllowSBurst(req.ClientIP(), 5) {
return nil, errors.New("rate limited")
}
return "ok", nil
}
func main() {
cfg, _ := config.LoadSingleFileConfig("application.yml")
builder := wf.NewBuilder(cfg)
builder.Service(&ratelimit.RateLimit{})
builder.Rest(&MyController{})
app := builder.Build()
app.Start()
}
Configuration (application.yml):
rate_limit:
limit: 600 # token fill interval (seconds)
burst: 5 # max bucket size
maxSize: 1000000
expiry: 3600 # cache expiry (seconds)
Authentication (auth)¶
Token-based authentication filter with login/logout:
Main features:
auth2.NewAuthenticationFilter(authenticator)- Create auth filterauth2.WithLogin()- Route metadata, marks routes requiring loginauth.User(req, ctx)- Get current logged-in user from request
Full example:
type UserController struct {
core.IService
authFilter *auth2.AuthenticationFilter
}
func (c *UserController) Init(ctx *core.Context) error {
c.authFilter = wf.GetFilter[*auth2.AuthenticationFilter](ctx)
// Login endpoints (no auth required)
ctx.Post("/signIn", c.signIn)
ctx.Post("/logout", c.logout)
// Business endpoints (auth required)
ctx.Get("/users", c.List).WithMeta(auth2.WithLogin())
return nil
}
func (c *UserController) signIn(req *web.Request) (any, error) {
// Verify credentials, then sign in
loginUser := &LoginUser{Id: user.Id, IsAdmin: user.IsAdmin}
return c.authFilter.SignIn(loginUser, req)
}
func (c *UserController) logout(req *web.Request) (any, error) {
return c.authFilter.SignOut(req)
}
Register the auth filter to a RestGroup:
restGroupBuilder := core.NewRestGroupBuilder()
restGroupBuilder.Rest(&UserController{})
restGroupBuilder.Port(8081)
restGroupBuilder.Filter(auth2.NewAuthenticationFilter(&MyAuthenticator{}))
builder.RestGroup(restGroupBuilder.Build())
Scheduled Tasks (schedule)¶
Cron expression-based task scheduling:
Main features:
schedule.NewScheduleWithSeconds()- Create second-precision schedulerschedule.AddFunc(cronExpr, func)- Add a scheduled taskschedule.AddIdOrReplaceKeyFunc(id, key, cronExpr, func)- Add by ID or replace by keyschedule.StopIdFunc(id)- Stop a specific taskschedule.GetIds()- Get all registered task IDs
See the Runner documentation for usage examples.
Other Components¶
| Component | Package | Description |
|---|---|---|
| In-memory Cache | component/cache |
High-performance cache based on Otter, supports TTL |
| Local Cache | component/localcache |
File-based local cache, supports Base64 file storage |
| Captcha | component/captcha |
Slider captcha generation and verification |
| Input Validation | component/validator |
Input validation based on go-playground/validator |
| CORS | component/cors |
Cross-origin resource sharing filter |
In-Memory Cache (cache)¶
High-performance cache based on Otter:
import "github.com/chuccp/go-web-frame/component/cache"
type MyController struct {
core.IService
cache *cache.Cache
}
func (c *MyController) Init(ctx *core.Context) error {
c.cache = wf.GetService[*cache.Cache](ctx)
ctx.Get("/api/data", c.Handle)
return nil
}
func (c *MyController) Handle(req *web.Request) (any, error) {
// Set cache
c.cache.Set("key", "value")
// Get cache
val, exists := c.cache.Get("key")
// Set if not exists (with TTL)
c.cache.SetNX("temp", "data", 5*time.Minute)
// Compute if absent
c.cache.ComputeIfAbsent("expensive_key", func() {
// Expensive computation, result auto-cached
})
// Invalidate cache
c.cache.Invalidate("key")
// Get cache stats
stats := c.cache.Stats()
return val, nil
}
Register:
Local Cache (localcache)¶
File-based local cache for caching generated files (images, PDFs, etc.):
import "github.com/chuccp/go-web-frame/component/localcache"
type MyController struct {
core.IService
localCache *localcache.LocalCache
}
func (c *MyController) Init(ctx *core.Context) error {
c.localCache = wf.GetService[*localcache.LocalCache](ctx)
ctx.Get("/report", c.GetReport)
return nil
}
func (c *MyController) GetReport(req *web.Request) (any, error) {
file, err := c.localCache.GetFile(func(value ...any) ([]byte, error) {
// Generate file content
return generateReport()
}, "report", "2024")
if err != nil {
return nil, err
}
return file, nil
}
Configuration:
Register:
Slider Captcha (captcha)¶
Slider captcha generation and verification:
import "github.com/chuccp/go-web-frame/component/captcha"
type CaptchaController struct {
core.IService
captcha *captcha.Captcha
}
func (c *CaptchaController) Init(ctx *core.Context) error {
c.captcha = wf.GetService[*captcha.Captcha](ctx)
ctx.Get("/captcha", c.GetCaptcha)
ctx.Post("/captcha/verify", c.Verify)
return nil
}
func (c *CaptchaController) GetCaptcha(req *web.Request) (any, error) {
data, err := c.captcha.GetCaptchaData()
if err != nil {
return nil, err
}
return data, nil
}
func (c *CaptchaController) Verify(req *web.Request) (any, error) {
thumbCode := req.Query("thumbCode")
x := req.Query("x")
data, valid := c.captcha.ValidateThumb(thumbCode, x)
if !valid {
return nil, errors.New("captcha verification failed")
}
return web.Data(data), nil
}
Configuration:
captcha:
codeKey: "your-encryption-key" # AES key (16 bytes)
codeIv: "your-encryption-iv" # AES IV (16 bytes)
Register:
Input Validation (validator)¶
Based on go-playground/validator with custom rules:
import "github.com/chuccp/go-web-frame/component/validator"
type MyController struct {
core.IService
validator *validator.Validator
}
func (c *MyController) Init(ctx *core.Context) error {
c.validator = wf.GetService[*validator.Validator](ctx)
ctx.Post("/users", c.Create)
return nil
}
type CreateUserInput struct {
Name string `json:"name" validate:"required"`
Phone string `json:"phone" validate:"mobile"` // Chinese mobile validation
Password string `json:"password" validate:"password"` // Strong password validation
}
func (c *MyController) Create(req *web.Request) (any, error) {
var input CreateUserInput
if err := req.BindJSON(&input); err != nil {
return nil, err
}
if err := c.validator.Validate(input); err != nil {
return nil, err
}
return web.Ok(), nil
}
Built-in validation rules:
| Rule | Description |
|---|---|
mobile |
Validate Chinese mobile numbers (supports +86 prefix, spaces, hyphens) |
password |
Strong password (min 8 chars, requires upper, lower, and digit) |
Register:
CORS Filter (cors)¶
Cross-origin resource sharing filter based on gin-contrib/cors:
import "github.com/chuccp/go-web-frame/component/cors"
// Register CORS filter
builder.Filter(cors.NewCrosFilter())
Default policy:
- Allow all origins (
AllowOriginFunc: returns true) - Allow credentials (
AllowCredentials: true) - Allowed headers:
Origin,Content-Length,Content-Type,Authorization - Auto-handle OPTIONS preflight requests
Redis Component (redis)¶
Redis client component based on go-redis/v9:
import "github.com/chuccp/go-web-frame/redis"
type MyController struct {
core.IService
redisClient *redis.Component
}
func (c *MyController) Init(ctx *core.Context) error {
c.redisClient = wf.GetService[*redis.Component](ctx)
ctx.Get("/cache", c.GetCache)
ctx.Post("/cache", c.SetCache)
return nil
}
func (c *MyController) GetCache(req *web.Request) (any, error) {
client := c.redisClient.GetClient()
val, err := client.Get(req.Request().Context(), "key").Result()
if err != nil {
return nil, err
}
return val, nil
}
func (c *MyController) SetCache(req *web.Request) (any, error) {
client := c.redisClient.GetClient()
err := client.Set(req.Request().Context(), "key", "value", 10*time.Minute).Err()
if err != nil {
return nil, err
}
return web.Ok(), nil
}
Register:
Configuration:
GetClient() returns *github.com/redis/go-redis/v9.Client, supporting the full go-redis API.
Next Steps¶
- Runner - Background task runners
- Service - Business logic layer
- Filter/Middleware - HTTP request filtering