Golang中传递地址但值未更新的问题(Go Chi中间件相关)

Golang中传递地址但值未更新的问题(Go Chi中间件相关)

bundle := i18n.NewBundle(language.English)
	bundle.RegisterUnmarshalFunc("json", json.Unmarshal)

	lang := []string{"en-US", "fr"}

	for i := 0; i < len(lang); i++ {
		bundle.LoadMessageFile(fmt.Sprintf("json/%s.json", lang[I]))
	}
	localizer := i18n.NewLocalizer(bundle, "en-US")
	appConfig := handler.AppConfig{T: t, Localizer: localizer}

	//common.ChangeString(&str)
	r.Use(middlewares.I18n(bundle, &appConfig))

中间件

func I18n(bundle *i18n.Bundle, appConfig *handler.AppConfig) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		fn := func(w http.ResponseWriter, r *http.Request) {

			lang := r.URL.Query().Get("lang")

			if lang == "" {
				lang = "en-US"
				url, _ := common.FullUrl(r)
				fmt.Println(url)

				q := url.Query()
				q.Add("lang", lang)
				url.RawQuery = q.Encode()

				http.Redirect(w, r, url.String(), http.StatusPermanentRedirect)
			}

			accept := r.Header.Get("Accept-Language")
			fmt.Println("accept", accept)
			localizer := i18n.NewLocalizer(bundle, lang, accept)
			appConfig.Localizer = localizer
			appConfig.Lang = lang
			next.ServeHTTP(w, r)
		}
		return http.HandlerFunc(fn)
	}
}

inside some handlers...
fmt.Println(l.config.Lang) //it is empty - what is wrong here?

更多关于Golang中传递地址但值未更新的问题(Go Chi中间件相关)的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

你好,

我尝试了那个方法。它运行得很好。使用中间件的上下文是最佳解决方案。 非常感谢

更多关于Golang中传递地址但值未更新的问题(Go Chi中间件相关)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,

能否在代码旁边添加一些描述? 你正在使用哪些包?

  • i18n
  • handler

这些包来自哪里?

你好, 我在想,一个更好的方法是否应该是中间件将本地化器添加到请求的 Context 中,而不是修改全局的 appConfig。 我认为这将是一个更好的方法,以避免多个请求以并发方式更改全局的 appConfig。

i18n => “github.com/nicksnyder/go-i18n/v2/i18n” handler => 自定义包,其中包含用于保存配置的结构体。

但是,看起来通过使用中间件,似乎无法更改值。因为所有内容都已编译,我只能获取到旧数据。我不太确定具体发生了什么。

目前,我创建了一个新的包来处理 i18n(本地包也命名为 i18n。我需要考虑并重命名它)。

我已经为该包实现了翻译方法:

func (i18 *I18n) Translate(text string) string {
	fmt.Println("Translate..", i18)
	localizer := i18n.NewLocalizer(i18.Bundle, i18.Lang)
	str, err := localizer.Localize(&i18n.LocalizeConfig{MessageID: text})
	if err != nil {
		return err.Error()
	}

	return str
}

我将 i18n 结构体传递给我的所有模板。在模板内部,我调用 translate 函数。

{{translate "login"}} // 翻译为“登录” 我设法让它工作了。

// 如果你真的需要代码,我可以创建一个简单的 Go Chi 中间件并分享代码。就像更改字符串的值。我尝试过,那也不行。(即使在传递地址之后)打印出来的仍然是旧值。

问题出现在中间件中修改的appConfig指针指向的值与处理器中访问的appConfig不是同一个实例。在Go Chi中间件链中,每个请求都会创建新的请求上下文,但您传递的appConfig指针在中间件中被修改后,处理器中访问的可能仍然是原始副本。

以下是修正后的代码示例:

// 1. 使用请求上下文传递配置
func I18n(bundle *i18n.Bundle, appConfig *handler.AppConfig) func(next http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        fn := func(w http.ResponseWriter, r *http.Request) {
            lang := r.URL.Query().Get("lang")
            
            if lang == "" {
                lang = "en-US"
                url, _ := common.FullUrl(r)
                q := url.Query()
                q.Add("lang", lang)
                url.RawQuery = q.Encode()
                http.Redirect(w, r, url.String(), http.StatusPermanentRedirect)
                return
            }
            
            accept := r.Header.Get("Accept-Language")
            localizer := i18n.NewLocalizer(bundle, lang, accept)
            
            // 创建配置副本并设置到上下文
            configCopy := *appConfig
            configCopy.Localizer = localizer
            configCopy.Lang = lang
            
            // 使用类型安全的上下文键
            type contextKey string
            const configKey contextKey = "appConfig"
            
            ctx := context.WithValue(r.Context(), configKey, &configCopy)
            next.ServeHTTP(w, r.WithContext(ctx))
        }
        return http.HandlerFunc(fn)
    }
}

// 2. 处理器中从上下文获取配置
func SomeHandler(w http.ResponseWriter, r *http.Request) {
    type contextKey string
    const configKey contextKey = "appConfig"
    
    if config, ok := r.Context().Value(configKey).(*handler.AppConfig); ok {
        fmt.Println(config.Lang) // 现在会正确输出语言代码
        // 使用config.Localizer进行本地化操作
    }
}

// 3. 路由设置
bundle := i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("json", json.Unmarshal)

langs := []string{"en-US", "fr"}
for _, lang := range langs {
    bundle.LoadMessageFile(fmt.Sprintf("json/%s.json", lang))
}

appConfig := &handler.AppConfig{
    T: t,
    Localizer: i18n.NewLocalizer(bundle, "en-US"),
}

r.Use(middlewares.I18n(bundle, appConfig))
r.Get("/some-path", SomeHandler)

关键修改:

  1. 中间件中创建appConfig的副本并设置新值
  2. 将配置指针存储到请求上下文中
  3. 处理器从上下文中检索配置
  4. 使用类型安全的上下文键避免键冲突

这样每个请求都会获得独立的配置副本,避免并发修改问题,同时确保处理器访问的是中间件更新后的值。

回到顶部