You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VS Code调试Go API时断点无法命中的问题求助

Go API项目断点无法命中排查

问题情况

调试简单Go程序时可正常命中断点,但在Go API项目中,API能正常响应Postman的POST请求(http://localhost:8080/api/dish/13),但设置在psqlrepo包insertOptionAvailable函数的断点始终无法触发。

项目核心代码

主函数代码

package main

import (
    "log"
    "os"
    // 省略其他导入
)

var (
    menuRepo       psqlrepo.MenuRepository   = psqlrepo.NewMenuRepository()
    menuService    service.MenuService       = service.NewMenuService(menuRepo)
    menuController controller.MenuController = controller.NewMenuController(menuService)

    dishRepo       psqlrepo.DishRepo         = psqlrepo.NewDishRepo()
    dishService    service.DishService       = service.NewDishService(dishRepo)
    dishController controller.DishController = controller.NewDishController(dishService)

    httpRouter router.Router = router.NewMuxRouter()
)

func main() {
    if err := godotenv.Load(); err != nil {
        log.Fatal(err)
    }
    httpRouter.POST("/api/menu/{id}", menuController.Create, middleware.ValidateToken)
    httpRouter.POST("/api/dish/{menu_id}", dishController.CreateDish, middleware.ValidateToken)
    httpRouter.SERVE(os.Getenv("PORT"))
}

仓库层代码(断点设置位置)

断点设置在insertOptionAvailable函数的第一个if语句处:

package psqlrepo

func insertOptionAvailable(optionAvailable entity.OptionsAvailables, optionID int, _db *sqlx.DB) error {
    var (
        itExist bool
    )
    if err := _db.QueryRow("select exists (select 1 from optionsAvailabe where name = $1)", optionAvailable.Name).Scan(&itExist); err != nil {
        return err
    }
    if itExist {
        return errors.New("this item already exists")
    }
    optionAvailable.CreatedAt = time.Now()
    optionAvailable.UpdatedAt = time.Now()
    _, err := _db.Exec("insert into optionsAvailabe(name, quantity, extra_price, option_id, updated_at, created_at) values($1, $2, $3, $4, $5, $6)", optionAvailable.Name, optionAvailable.Quantity, optionAvailable.ExtraPrice, optionID, optionAvailable.UpdatedAt, optionAvailable.CreatedAt)
    if err != nil {
        return err
    }
    return nil
}

func (*dishRepo) NewDish(dish entity.Dish, menuId int) (sql.Result, error) {
    var (
        _db        = db.NewPostgreSQLConection()
        itExist    bool
        dishID     int
        extraItems []entity.ExtraItem
    )
    defer _db.Close()
    if err := _db.QueryRow("select exists (select 1 from dishes where name=$1)", &dish.Name).Scan(&itExist); err != nil {
        return nil, err
    }
    if itExist {
        return nil, errors.New("dish already exists")
    }
    dish.CreatedAt = time.Now()
    dish.UpdatedAt = time.Now()
    err := _db.QueryRow("insert into dishes(name, description, tags, menu_id, price, created_at, updated_at) values($1, $2, $3, $4, $5, $6, $7) returning id", dish.Name, dish.Description, dish.Tags, menuId, dish.Price, dish.CreatedAt, dish.UpdatedAt).Scan(&dishID)
    if err != nil {
        return nil, err
    }
    if len(*dish.Ingredients) > 0 {
        for _, ingredient := range *dish.Ingredients {
            if err := insertIngredient(ingredient, dishID, _db); err != nil {
                return nil, err
            }

        }
    }
    if len(*dish.Extras) > 0 {
        for _, option := range *dish.Extras {
            optionID, err := insertOption(option, dishID, _db)
            if err != nil || optionID == -1 {
                return nil, err
            }
            extraItems = append(extraItems, option.ExtraItems...)
            if len(extraItems) > 0 {
                for _, _optionAvailable := range extraItems {
                    if err = insertOptionAvailable(_optionAvailable, optionID, _db); err != nil {
                        return nil, err
                    }
                }
            }
        }
    }
    return nil, nil
}

服务层代码

package service

import (
    "database/sql"
    // 省略其他导入
)

type DishService interface {
    NewDish(dish entity.Dish, menuId int) (sql.Result, error)
}

var (
    _dishRepo psqlrepo.DishRepo
)

type dishService struct{}

func NewDishService(dishRepo psqlrepo.DishRepo) DishService {
    _dishRepo = dishRepo
    return &dishService{}
}
func (*dishService) NewDish(dish entity.Dish, menuId int) (sql.Result, error) {
    return _dishRepo.NewDish(dish, menuId)
}

控制器层代码

package controller

import (
    "encoding/json"
    "net/http"
    "strconv"
    // 省略其他导入
)

type DishController interface {
    CreateDish(rw http.ResponseWriter, r *http.Request)
}
type dishController struct{}

var (
    _dishService service.DishService
)

func NewDishController(dishService service.DishService) DishController {
    _dishService = dishService
    return &dishController{}
}
func (*dishController) CreateDish(rw http.ResponseWriter, r *http.Request) {
    r.Header.Set("Content Type", "json/application")
    var (
        dish     entity.Dish
        response entity.Reponse
        vars     = mux.Vars(r)
    )
    menuID := vars["menu_id"]
    if menuID == "" {
        http.Error(rw, "menu id required", http.StatusBadRequest)
        return
    }
    id, err := strconv.Atoi(menuID)
    if err != nil {
        http.Error(rw, "error with menu id", http.StatusBadRequest)
        return
    }
    if err := json.NewDecoder(r.Body).Decode(&dish); err != nil {
        http.Error(rw, "Error getting body", http.StatusBadRequest)
        return
    }
    result, err := _dishService.NewDish(dish, id)
    if err != nil {
        http.Error(rw, err.Error(), http.StatusBadRequest)
        return
    }
    response = entity.Reponse{
        Message: "Dish created successfully",
        Error:   "",
        Data:    result,
    }
    rw.WriteHeader(http.StatusOK)
    json.NewEncoder(rw).Encode(&response)
}

当前launch.json配置

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Package",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${workspaceFolder}"
        },
        {
            "name": "Launch file",
            "type": "go",
            "request": "launch",
            "mode": "debug",
            "program": "${file}"
        },
        {
            "name": "Attach to Process",
            "type": "go",
            "request": "attach",
            "mode": "local",
            "processId": 0
        },
    ]
}

环境信息

  • 设备:M2 MacBook Air
  • 已安装适配Apple Silicon的Go和VS Code版本

排查步骤

  1. 调整调试编译模式
    修改launch.json中"Launch Package"配置的mode为debug,避免auto模式自动选择release编译(无调试信息):

    {
        "name": "Launch Package",
        "type": "go",
        "request": "launch",
        "mode": "debug",
        "program": "${workspaceFolder}"
    }
    

    执行go clean -cache清理编译缓存,确保生成带调试信息的二进制文件。

  2. 验证代码执行路径

    • 检查Postman请求体是否包含Extras字段,且Extras下的ExtraItems不为空——只有这部分逻辑会触发insertOptionAvailable函数。
    • 在NewDish函数开头、调用insertOptionAvailable的循环处添加临时断点,确认代码是否走到该分支。
  3. 更新调试工具与插件

    • 确保VS Code的Go插件为最新版本。
    • 更新Delve调试工具:执行go install github.com/go-delve/delve/cmd/dlv@latest,然后执行dlv version确认架构为arm64。
  4. 适配Apple Silicon环境

    • 关闭VS Code的Rosetta模式:右键VS Code图标→显示简介→取消勾选"使用Rosetta打开"。
    • 确认Go环境变量GOARCH为arm64,执行go env GOARCH查看输出。
  5. 检查接口实现与变量初始化

    • 确认psqlrepo.NewDishRepo()返回的实例正确实现了DishRepo接口,避免调用到未预期的代码分支。
    • 检查全局变量_dishRepo、_dishService的初始化是否正确,确保实际调用的是预期的实现。

内容的提问来源于stack exchange,提问作者RoG Inz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 17:45:53