showliuzp 2025-09-23 16:48 采纳率: 84.3%
浏览 7
已结题

golang调用函数中间件


type Operator struct{
    LogTag string
}

func NewOperator() *Operator{
    obj := new(Operator)
    obj.LogTag = "services.admin"

    return obj
}

func (o *Operator) List(req *iapi.OperatorListReq, token *string)(resp iapi.OperatorListResp,err error){
    token_detail,err := ParseToken(token)
    if err != nil{
        return
    }


    return
}

func (o *Operator) Add(req *iapi.OperatorAddReq, token *string)(resp iapi.OperatorAddResp,err error){
    token_detail,err := ParseToken(token)
    if err != nil{
        return
    }


    return
}


ParseToken为检查函数,每次执行操作必须要经过这个函数,但我不想每个函数里都这么调用,能否将这个函数写个中间件
  • 写回答

5条回答 默认 最新

  • 檀越@新空间 2025-09-23 16:49
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    在 Go 语言中,虽然没有像其他语言(如 Python 或 Node.js)那样的“中间件”机制,但你可以通过函数组合装饰器模式来实现类似“中间件”的功能。这样可以避免在每个方法中重复调用 ParseToken 函数。


    ✅ 解决方案

    1. 定义一个通用的中间件函数

    你可以创建一个包装函数,用于执行 ParseToken 检查,并返回结果。这个函数可以接受一个操作函数作为参数,然后在其前后执行检查逻辑。

    func WithAuth(fn func(*Operator, *iapi.OperatorListReq, *string) (iapi.OperatorListResp, error)) func(*Operator, *iapi.OperatorListReq, *string) (iapi.OperatorListResp, error) {
        return func(o *Operator, req *iapi.OperatorListReq, token *string) (iapi.OperatorListResp, error) {
            token_detail, err := ParseToken(token)
            if err != nil {
                // 如果解析失败,直接返回错误
                return nil, err
            }
    
            // 执行原始函数
            return fn(o, req, token)
        }
    }
    

    重点: 这个 WithAuth 中间件函数将 ParseToken 的逻辑封装起来,可以在多个操作中复用。


    2. 使用中间件包装你的方法

    你可以在调用 ListAdd 方法时,使用 WithAuth 来包装它们,从而避免在每个方法内部重复写 ParseToken

    // 原始方法
    func (o *Operator) List(req *iapi.OperatorListReq, token *string) (iapi.OperatorListResp, error) {
        // 不再需要 ParseToken
        return iapi.OperatorListResp{}, nil
    }
    
    func (o *Operator) Add(req *iapi.OperatorAddReq, token *string) (iapi.OperatorAddResp, error) {
        // 不再需要 ParseToken
        return iapi.OperatorAddResp{}, nil
    }
    

    然后,你在调用这些方法时使用中间件:

    func CallList(o *Operator, req *iapi.OperatorListReq, token *string) (iapi.OperatorListResp, error) {
        return WithAuth(o.List)(o, req, token)
    }
    
    func CallAdd(o *Operator, req *iapi.OperatorAddReq, token *string) (iapi.OperatorAddResp, error) {
        return WithAuth(o.Add)(o, req, token)
    }
    

    重点: 通过这种方式,你可以将认证逻辑集中处理,提高代码可维护性。


    3. 进一步优化:使用接口和泛型(Go 1.18+)

    如果你希望更通用一些,可以考虑使用泛型来支持不同类型的请求和响应。

    type HandlerFunc[T any, R any] func(*Operator, T, *string) (R, error)
    
    func WithAuth[T any, R any](fn HandlerFunc[T, R]) HandlerFunc[T, R] {
        return func(o *Operator, req T, token *string) (R, error) {
            token_detail, err := ParseToken(token)
            if err != nil {
                var zero R
                return zero, err
            }
    
            return fn(o, req, token)
        }
    }
    

    然后在方法中使用:

    func (o *Operator) List(req *iapi.OperatorListReq, token *string) (iapi.OperatorListResp, error) {
        return iapi.OperatorListResp{}, nil
    }
    
    // 调用方式:
    resp, err := WithAuth(func(o *Operator, req *iapi.OperatorListReq, token *string) (iapi.OperatorListResp, error) {
        return o.List(req, token)
    })(o, req, token)
    

    重点: 使用泛型可以让中间件更加灵活,适用于不同的方法。


    ✅ 总结

    | 步骤 | 内容 | |------|------| | 1 | 定义一个通用的中间件函数 WithAuth,用于封装 ParseToken 逻辑 | | 2 | 在调用方法前使用 WithAuth 包装原方法,避免重复代码 | | 3 | 可选:使用泛型提高灵活性,适配不同类型的方法 |


    ✅ 示例代码(完整版)

    package main
    
    import (
    	"fmt"
    )
    
    // 假设的类型定义
    type Operator struct {
    	LogTag string
    }
    
    type OperatorListReq struct{}
    type OperatorListResp struct{}
    type OperatorAddReq struct{}
    type OperatorAddResp struct{}
    
    // ParseToken 是验证 Token 的函数
    func ParseToken(token *string) (interface{}, error) {
    	if token == nil || *token == "" {
    		return nil, fmt.Errorf("invalid token")
    	}
    	return "valid", nil
    }
    
    // 中间件函数
    func WithAuth[T any, R any](fn func(*Operator, T, *string) (R, error)) func(*Operator, T, *string) (R, error) {
    	return func(o *Operator, req T, token *string) (R, error) {
    		tokenDetail, err := ParseToken(token)
    		if err != nil {
    			var zero R
    			return zero, err
    		}
    		return fn(o, req, token)
    	}
    }
    
    // 原始方法
    func (o *Operator) List(req *OperatorListReq, token *string) (OperatorListResp, error) {
    	fmt.Println("List method called")
    	return OperatorListResp{}, nil
    }
    
    func (o *Operator) Add(req *OperatorAddReq, token *string) (OperatorAddResp, error) {
    	fmt.Println("Add method called")
    	return OperatorAddResp{}, nil
    }
    
    // 使用中间件调用方法
    func CallList(o *Operator, req *OperatorListReq, token *string) (OperatorListResp, error) {
    	return WithAuth(o.List)(o, req, token)
    }
    
    func CallAdd(o *Operator, req *OperatorAddReq, token *string) (OperatorAddResp, error) {
    	return WithAuth(o.Add)(o, req, token)
    }
    
    func main() {
    	operator := &Operator{}
    	token := "valid_token"
    
    	resp, err := CallList(operator, &OperatorListReq{}, token)
    	if err != nil {
    		fmt.Println("Error:", err)
    	} else {
    		fmt.Println("List response:", resp)
    	}
    
    	respAdd, err := CallAdd(operator, &OperatorAddReq{}, token)
    	if err != nil {
    		fmt.Println("Error:", err)
    	} else {
    		fmt.Println("Add response:", respAdd)
    	}
    }
    

    重点: 通过上述方式,你可以将 ParseToken 集中处理,避免重复代码,提升代码可读性和可维护性。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

问题事件

  • 系统已结题 10月1日
  • 已采纳回答 9月23日
  • 创建了问题 9月23日