自定义处理器
当预置 API 无法满足需求时,可以创建自定义处理器。
基本自定义 API
定义自定义 API
go
package resources
import (
"github.com/ilxqx/vef-framework-go/api"
"github.com/gofiber/fiber/v2"
)
type UserResource struct {
api.Resource
}
func NewUserResource() api.Resource {
resource := &UserResource{
Resource: api.NewResource("smp/sys/user"),
}
// Register custom API
resource.RegisterApi("reset_password", resource.ResetPassword)
resource.RegisterApi("change_status", resource.ChangeStatus)
return resource
}实现自定义处理器
go
// ResetPassword custom API handler
func (r *UserResource) ResetPassword(ctx fiber.Ctx) error {
// Parse parameters
var params struct {
UserId string `json:"userId" validate:"required"`
NewPassword string `json:"newPassword" validate:"required,min=8"`
}
if err := ctx.BodyParser(¶ms); err != nil {
return api.Error(ctx, "Invalid parameters")
}
// Validate parameters
if err := api.Validate(¶ms); err != nil {
return api.ValidationError(ctx, err)
}
// Business logic
hashedPassword, _ := bcrypt.GenerateFromPassword(
[]byte(params.NewPassword),
bcrypt.DefaultCost,
)
result := orm.DB().Model(&models.User{}).
Where("id = ?", params.UserId).
Update("password", string(hashedPassword))
if result.Error != nil {
return api.Error(ctx, "Failed to reset password")
}
return api.Success(ctx, nil)
}参数注入
自动参数解析
go
type ResetPasswordParams struct {
UserId string `json:"userId" validate:"required"`
NewPassword string `json:"newPassword" validate:"required,min=8"`
}
func (r *UserResource) ResetPassword(ctx fiber.Ctx, params ResetPasswordParams) error {
// params is automatically parsed and validated
hashedPassword, _ := bcrypt.GenerateFromPassword(
[]byte(params.NewPassword),
bcrypt.DefaultCost,
)
result := orm.DB().Model(&models.User{}).
Where("id = ?", params.UserId).
Update("password", string(hashedPassword))
if result.Error != nil {
return api.Error(ctx, "Failed to reset password")
}
return api.Success(ctx, nil)
}资源字段注入
go
// Resource field is automatically injected
func (r *UserResource) GetStatistics(ctx fiber.Ctx) error {
resource := r.Resource // Access resource information
var stats struct {
TotalUsers int64 `json:"totalUsers"`
ActiveUsers int64 `json:"activeUsers"`
}
orm.DB().Model(&models.User{}).Count(&stats.TotalUsers)
orm.DB().Model(&models.User{}).Where("is_active", true).Count(&stats.ActiveUsers)
return api.Success(ctx, stats)
}依赖注入
注入服务
go
type UserResource struct {
api.Resource
emailService *services.EmailService
cacheService *services.CacheService
}
func NewUserResource(
emailService *services.EmailService,
cacheService *services.CacheService,
) api.Resource {
resource := &UserResource{
Resource: api.NewResource("smp/sys/user"),
emailService: emailService,
cacheService: cacheService,
}
resource.RegisterApi("send_verification", resource.SendVerification)
return resource
}
func (r *UserResource) SendVerification(ctx fiber.Ctx, params struct {
UserId string `json:"userId" validate:"required"`
}) error {
// Use injected services
user, err := r.cacheService.GetUser(params.UserId)
if err != nil {
return api.Error(ctx, "User not found")
}
if err := r.emailService.SendVerificationEmail(user.Email); err != nil {
return api.Error(ctx, "Failed to send email")
}
return api.Success(ctx, nil)
}权限控制
设置 API 权限
go
func NewUserResource() api.Resource {
resource := &UserResource{
Resource: api.NewResource("smp/sys/user"),
}
// Register API with permission
resource.RegisterApiWithPermission(
"reset_password",
"user:reset_password",
resource.ResetPassword,
)
return resource
}手动权限检查
go
func (r *UserResource) AdminOperation(ctx fiber.Ctx) error {
// Check permission manually
currentUser := contextx.GetCurrentUser(ctx)
if !currentUser.HasPermission("admin:manage") {
return api.Forbidden(ctx, "Permission denied")
}
// Perform admin operation
return api.Success(ctx, nil)
}响应格式
成功响应
go
// Return data
return api.Success(ctx, map[string]interface{}{
"id": user.Id,
"username": user.Username,
})
// Return with message
return api.SuccessWithMessage(ctx, "Operation completed", data)
// Return empty success
return api.Success(ctx, nil)错误响应
go
// General error
return api.Error(ctx, "Something went wrong")
// Validation error
return api.ValidationError(ctx, validationErrors)
// Not found
return api.NotFound(ctx, "User not found")
// Forbidden
return api.Forbidden(ctx, "Permission denied")
// Custom status code
return api.ErrorWithCode(ctx, 400, "Bad request")事务处理
go
func (r *UserResource) TransferBalance(ctx fiber.Ctx, params struct {
FromUserId string `json:"fromUserId" validate:"required"`
ToUserId string `json:"toUserId" validate:"required"`
Amount float64 `json:"amount" validate:"required,gt=0"`
}) error {
// Execute in transaction
err := orm.Transaction(func(tx *gorm.DB) error {
// Deduct from sender
if err := tx.Model(&models.User{}).
Where("id = ?", params.FromUserId).
Update("balance", gorm.Expr("balance - ?", params.Amount)).Error; err != nil {
return err
}
// Add to receiver
if err := tx.Model(&models.User{}).
Where("id = ?", params.ToUserId).
Update("balance", gorm.Expr("balance + ?", params.Amount)).Error; err != nil {
return err
}
return nil
})
if err != nil {
return api.Error(ctx, "Transfer failed")
}
return api.Success(ctx, nil)
}完整示例
go
package resources
import (
"my-app/internal/modules/user/models"
"my-app/internal/services"
"github.com/ilxqx/vef-framework-go/api"
"github.com/ilxqx/vef-framework-go/apis"
"github.com/ilxqx/vef-framework-go/contextx"
"github.com/ilxqx/vef-framework-go/orm"
"github.com/gofiber/fiber/v2"
"golang.org/x/crypto/bcrypt"
)
type UserResource struct {
api.Resource
apis.FindPageApi[models.User, payloads.UserSearch]
apis.CreateApi[models.User, payloads.UserParams]
emailService *services.EmailService
}
func NewUserResource(emailService *services.EmailService) api.Resource {
resource := &UserResource{
Resource: api.NewResource("smp/sys/user"),
emailService: emailService,
FindPageApi: apis.NewFindPageApi[models.User, payloads.UserSearch](),
CreateApi: apis.NewCreateApi[models.User, payloads.UserParams](),
}
// Register custom APIs
resource.RegisterApiWithPermission("reset_password", "user:reset_password", resource.ResetPassword)
resource.RegisterApiWithPermission("change_status", "user:update", resource.ChangeStatus)
resource.RegisterApi("get_profile", resource.GetProfile)
return resource
}
func (r *UserResource) ResetPassword(ctx fiber.Ctx, params struct {
UserId string `json:"userId" validate:"required"`
NewPassword string `json:"newPassword" validate:"required,min=8"`
}) error {
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(params.NewPassword), bcrypt.DefaultCost)
result := orm.DB().Model(&models.User{}).
Where("id = ?", params.UserId).
Update("password", string(hashedPassword))
if result.Error != nil {
return api.Error(ctx, "Failed to reset password")
}
// Send notification email
go r.emailService.SendPasswordResetNotification(params.UserId)
return api.Success(ctx, nil)
}
func (r *UserResource) ChangeStatus(ctx fiber.Ctx, params struct {
UserId string `json:"userId" validate:"required"`
IsActive bool `json:"isActive"`
}) error {
result := orm.DB().Model(&models.User{}).
Where("id = ?", params.UserId).
Update("is_active", params.IsActive)
if result.Error != nil {
return api.Error(ctx, "Failed to change status")
}
return api.Success(ctx, nil)
}
func (r *UserResource) GetProfile(ctx fiber.Ctx) error {
currentUser := contextx.GetCurrentUser(ctx)
var user models.User
if err := orm.DB().
Preload("Department").
Preload("Roles").
First(&user, "id = ?", currentUser.Id).Error; err != nil {
return api.NotFound(ctx, "User not found")
}
// Remove sensitive data
user.Password = ""
return api.Success(ctx, user)
}