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

跨包传递context.Context存取值:数据库事务传递失败排查

问题原因及解决方法

核心问题

context.WithValue 不会修改原有Context,而是返回一个包含新值的全新Context实例。你在service层调用该方法后,既没有将新Context返回给上层,也没有用它执行后续操作,导致server层的原始Context完全没带上事务数据,自然拿不到值。

修复步骤

1. 定义Context的自定义Key(避免冲突)

不要直接用字符串作为key,建议在项目里定义专属的key类型:

// 可放在公共包(比如contexts包)中
package contexts

type ctxKey string

const TransactionKey ctxKey = "transaction"

2. 修改Service层,返回新Context并使用事务执行操作

service层需要返回新的Context,并且要用开启的事务执行数据库操作,否则事务不会生效:

package services

import (
    "context"
    "e-commerce/models"
    "e-commerce/contexts" // 引入自定义key的包
)

func (s Service) CreateCategory(ctx context.Context, category *models.Category) (context.Context, *models.Category, error) {
    err := category.Validate()
    if err != nil {
        return ctx, nil, err
    }

    tx := s.userRepository.Db().Begin()
    // 创建携带事务的新Context
    newCtx := context.WithValue(ctx, contexts.TransactionKey, tx)

    // 用事务tx执行Create,而非原Db()实例
    result := tx.Create(&category)
    if result.Error != nil {
        tx.Rollback() // 出错先回滚
        return newCtx, nil, result.Error
    }

    return newCtx, category, nil
}

3. 修改Server层,接收新Context并传递给响应方法

在server的CreateCategory方法中,接收service返回的新Context,再传给returnResponse:

package server

import (
    "e-commerce/models"
    "e-commerce/contexts"
    "encoding/json"
    "net/http"
)

func (s *Server) CreateCategory(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
    var category models.Category

    err := json.NewDecoder(req.Body).Decode(&category)
    if err != nil {
        returnResponse(ctx, w, http.StatusBadRequest, err)
        return
    }

    // 接收service返回的新Context
    newCtx, _, err := s.service.CreateCategory(ctx, &category)
    if err != nil {
        returnResponse(newCtx, w, http.StatusBadRequest, err)
        return
    }

    returnResponse(newCtx, w, http.StatusOK, err)
}

4. 完善响应方法的空值判断

避免nil panic,先判断事务是否存在:

package server

import (
    "context"
    "e-commerce/config"
    "e-commerce/services"
    "e-commerce/contexts"
    "github.com/gorilla/mux"
    "gorm.io/gorm"
    "net/http"
)

func returnResponse(ctx context.Context, w http.ResponseWriter, status int, err error) {
    tx, ok := ctx.Value(contexts.TransactionKey).(*gorm.DB)
    if ok {
        if status != http.StatusOK {
            tx.Rollback()
        } else {
            tx.Commit()
        }
    }

    w.WriteHeader(status)
    if err != nil {
        w.Write([]byte(err.Error()))
    }
}

额外提示

  • 事务的生命周期建议由上层(如server层)管理,service层专注业务逻辑,职责划分更清晰。
  • 若多个service方法需要共享同一事务,务必确保传递的是携带事务的Context。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 10:26:02