Skip to content

Latest commit

 

History

History
167 lines (132 loc) · 5.51 KB

File metadata and controls

167 lines (132 loc) · 5.51 KB

API错误分析与解决方案

问题描述

在调用聊天记录查询API时出现以下错误:

{"@timestamp":"2025-04-13T13:26:59.039+08:00","caller":"resultx/httpResponse.go:61","content":"【im】 err field \"conversationId\" is not set","level":"error","span":"7c20b19360c876ab","trace":"d63018a2b4072d97685e8206df695035"}

实际请求URL为:

GET /v1/im/chatlog?conversationId=0x0000003000000006_0x0000003000000002&startSendTime=&endSendTime&count=20

问题分析

  1. 错误信息:系统报告field "conversationId" is not set,表明必填字段conversationId未被正确设置
  2. 请求参数:URL中确实包含conversationId参数
  3. 参数格式:参数值为0x0000003000000006_0x0000003000000002,包含特殊字符和十六进制值

代码分析

// ChatLogReq结构定义 (types.go)
type ChatLogReq struct {
    ConversationId string `json:"conversationId"`
    StartSendTime  int64  `json:"startSendTime,omitempty"`
    EndSendTime    int64  `json:"endSendTime,omitempty"`
    Count          int64  `json:"count,omitempty"`
}

// 参数解析代码 (getChatLogHandler.go)
func getChatLogHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req types.ChatLogReq
        if err := httpx.Parse(r, &req); err != nil {
            httpx.ErrorCtx(r.Context(), w, err)
            return
        }
        // ...省略后续代码
    }
}

原因分析

  1. 参数解析机制httpx.Parse在处理GET请求时,会尝试从URL查询参数中提取数据映射到结构体
  2. 标签不匹配:go-zero框架可能对查询参数名和JSON标签名的处理存在差异
  3. 特殊字符处理:conversationId中包含下划线等特殊字符,可能在URL参数解析过程中出现问题

解决方案

方案一:修改handler代码,手动提取参数

func getChatLogHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req types.ChatLogReq
        
        // 手动获取URL参数
        req.ConversationId = r.URL.Query().Get("conversationId")
        
        // 如果conversationId为空,返回错误
        if req.ConversationId == "" {
            httpx.ErrorCtx(r.Context(), w, errors.New("缺少会话ID参数"))
            return
        }
        
        // 提取其他可选参数
        if startTime := r.URL.Query().Get("startSendTime"); startTime != "" {
            startTimeInt, err := strconv.ParseInt(startTime, 10, 64)
            if err == nil {
                req.StartSendTime = startTimeInt
            }
        }
        
        if endTime := r.URL.Query().Get("endSendTime"); endTime != "" {
            endTimeInt, err := strconv.ParseInt(endTime, 10, 64)
            if err == nil {
                req.EndSendTime = endTimeInt
            }
        }
        
        if count := r.URL.Query().Get("count"); count != "" {
            countInt, err := strconv.ParseInt(count, 10, 64)
            if err == nil {
                req.Count = countInt
            }
        }
        
        l := logic.NewGetChatLogLogic(r.Context(), svcCtx)
        resp, err := l.GetChatLog(&req)
        if err != nil {
            httpx.ErrorCtx(r.Context(), w, err)
        } else {
            httpx.OkJsonCtx(r.Context(), w, resp)
        }
    }
}

方案二:调整结构体标签

type ChatLogReq struct {
    ConversationId string `json:"conversationId" form:"conversationId"`
    StartSendTime  int64  `json:"startSendTime,omitempty" form:"startSendTime,optional"`
    EndSendTime    int64  `json:"endSendTime,omitempty" form:"endSendTime,optional"`
    Count          int64  `json:"count,omitempty" form:"count,optional"`
}

方案三:修改API设计为路径参数

// API定义修改
// 从: GET /chatlog (ChatLogReq) returns (ChatLogResp)
// 到: GET /chatlog/:conversationId (ChatLogReq) returns (ChatLogResp)

// 然后修改handler
func getChatLogHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req types.ChatLogReq
        req.ConversationId = chi.URLParam(r, "conversationId")  // 使用chi路由或其他库获取路径参数
        
        // 解析其他查询参数
        if err := httpx.Parse(r, &req); err != nil {
            httpx.ErrorCtx(r.Context(), w, err)
            return
        }
        
        // ...其他代码不变
    }
}

解决方案选择

最优方案是方案二:修改API定义文件中的类型定义,为结构体字段添加form标签。这样当代码重新生成时,生成的代码已包含正确的参数解析能力,不会因为重新生成而丢失修复。

最终解决方案(修改API定义文件)

im.api文件中为ChatLogReq结构体添加form标签:

type (
    ChatLogReq {
        ConversationId string `json:"conversationId" form:"conversationId"`
        StartSendTime  int64  `json:"startSendTime,omitempty" form:"startSendTime,optional"`
        EndSendTime    int64  `json:"endSendTime,omitempty" form:"endSendTime,optional"`
        Count          int64  `json:"count,omitempty" form:"count,optional"`
    }
)

修改后运行以下命令重新生成代码:

goctl api go -api im.api -dir .

这种方式的优势是:

  1. 修复永久生效,不会被代码重新生成覆盖
  2. 遵循go-zero框架的设计理念
  3. 不需要手动修改生成的处理函数
  4. 同时兼容JSON请求体和URL查询参数