Golang中如何实现HTTP服务器为每个客户端单独保存数据

Golang中如何实现HTTP服务器为每个客户端单独保存数据 我有一个简单的HTTP服务器,只有一个路由。有人能帮我设置服务器,使得当我为每个连接的客户端调用路由localhost:8090/api//m_test时,它能分别为每个连接保持随机值吗?我打开了两个会话,一个在Postman中,一个在浏览器中,结果我从前一个映射中获取了来自每个客户端的全部值。我是需要单独配置服务器,还是需要设置通道来分离数据?

这些值似乎是为每个连接的客户端追加的,例如: { “current”: “jZjHwForIZ”, “previous”: [ “pPevdwCPHD”, “pcifcDJtDg”, “eHemlejDlt”, “nlEGpitRVL”, “xChOocByrP” ] }

代码如下:

package main

import (
	"github.com/gin-gonic/gin"
	"src/src/textfiles"
)

func main() {

	router := initRouter()

	router.Run(":8090")

}
func initRouter() *gin.Engine {

	router := gin.Default()

	router.Use(gin.Logger())
	router.Use(gin.Recovery())
	api := router.Group("/api")

	api.GET("/m_test", textfiles.RandomTexts)

	return router
}
func CORSMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {

		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Credentials", "false")
		c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
		c.Header("Access-Control-Allow-Methods", "POST,HEAD,PATCH, OPTIONS, GET, PUT")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		c.Next()
	}
}
package textfiles

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"math/rand"
	"net/http"
	"time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
var previousLetter = []string{}

//var v =""

func RandomTexts(context *gin.Context) {

	v := RandStringRunes(10)

	fmt.Println(v)

	context.JSON(http.StatusCreated, gin.H{"current": v, "previous": previousLetter})
	//context.
	previousLetter = append(previousLetter, v)

	fmt.Println(previousLetter)
}

func RandStringRunes(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}
func init() {
	rand.Seed(time.Now().UnixNano())
	previousLetter = []string{}
}

更多关于Golang中如何实现HTTP服务器为每个客户端单独保存数据的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

非常感谢。按照您的建议,它成功运行了。

更多关于Golang中如何实现HTTP服务器为每个客户端单独保存数据的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,Sibert,

感谢你的建议。我尝试了Dean提到的方法,现在可以正常工作了。

我遇到的问题是在客户端之间共享相同的数据。

我还未完全理解整个情况。但假设所有用户共享相同的数据,并且您想跟踪某个用户当前正在共享数据的哪一部分。可能需要在浏览器级别为用户缓存这些数据。使用 SessionStore、LocalStorage 还是 IndexedDB?是作为索引还是数据本身。

scarvelh:

我是否需要单独配置服务器或设置通道来分离数据?

如果我没理解错你的问题:每个数据库都有一个“密钥”。这个密钥是数据库的凭据,由IP地址、数据库名称、用户名和密码组成。每个数据库都有其自己的凭据。对于每次查询,你都必须发送正确的“密钥”以连接到正确的数据库。具体如何实现这一点,可能因你连接数据库的方式而异。

你好 Sibert,

如果我理解正确的话,你的意思是说我应该使用会话来知道该向谁发送正确的信息吗?我目前遇到的问题是在不同客户端之间共享相同的数据。

代码

previousLetter = append(previousLetter, v)

这段代码在每次调用 RandomTexts 函数时都会进行追加。我需要为每个连接的客户端(无论是 Postman 还是浏览器)提供不同的值。

previousLetter 是一个全局变量。听起来您想实现一种在Web开发中通常被称为“会话”或类似功能的东西。通常,从高层次来看,其流程大致如下:

  1. 客户端(浏览器)以某种方式向服务器标识自己(通常通过身份验证)。
  2. 服务器向客户端发送某种会话标识符(通常是JWT或某种生成的密钥/GUID)。
  3. 客户端将标识符存储在某个地方(通常在Cookie或本地存储中)。
  4. 在后续请求中,客户端发送该标识符(通常作为请求头),以便服务器知道该请求属于哪个会话。

问题在于使用了全局变量 previousLetter 来存储所有客户端的随机值。每个请求都会向同一个切片追加数据,导致数据在所有客户端间共享。以下是几种解决方案:

方案1:使用会话(Session)

安装会话管理库:

go get github.com/gin-contrib/sessions

修改代码:

package main

import (
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
    "src/src/textfiles"
)

func main() {
    router := initRouter()
    router.Run(":8090")
}

func initRouter() *gin.Engine {
    router := gin.Default()
    
    // 配置会话
    store := cookie.NewStore([]byte("secret"))
    router.Use(sessions.Sessions("mysession", store))
    
    router.Use(gin.Logger())
    router.Use(gin.Recovery())
    
    api := router.Group("/api")
    api.GET("/m_test", textfiles.RandomTexts)
    
    return router
}

修改 textfiles 包:

package textfiles

import (
    "github.com/gin-contrib/sessions"
    "github.com/gin-gonic/gin"
    "math/rand"
    "net/http"
    "time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandomTexts(context *gin.Context) {
    // 获取会话
    session := sessions.Default(context)
    
    // 从会话中获取之前的随机值
    var previousLetter []string
    if val := session.Get("previous"); val != nil {
        previousLetter = val.([]string)
    }
    
    // 生成新的随机值
    v := RandStringRunes(10)
    
    // 将新值添加到历史记录
    previousLetter = append(previousLetter, v)
    
    // 保存到会话
    session.Set("previous", previousLetter)
    session.Save()
    
    // 返回响应
    context.JSON(http.StatusCreated, gin.H{
        "current":  v,
        "previous": previousLetter,
    })
}

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}

func init() {
    rand.Seed(time.Now().UnixNano())
}

方案2:使用JWT令牌

安装JWT库:

go get github.com/golang-jwt/jwt/v4

修改 textfiles 包:

package textfiles

import (
    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v4"
    "math/rand"
    "net/http"
    "time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
var jwtSecret = []byte("your-secret-key")

type ClientData struct {
    Previous []string `json:"previous"`
    jwt.RegisteredClaims
}

func RandomTexts(context *gin.Context) {
    // 从Cookie或Header获取令牌
    tokenString := context.GetHeader("Authorization")
    if tokenString == "" {
        // 生成新令牌
        claims := &ClientData{
            Previous: []string{},
            RegisteredClaims: jwt.RegisteredClaims{
                ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
                IssuedAt:  jwt.NewNumericDate(time.Now()),
            },
        }
        
        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        tokenString, _ = token.SignedString(jwtSecret)
        
        // 设置响应头
        context.Header("X-Auth-Token", tokenString)
    } else {
        // 解析现有令牌
        token, err := jwt.ParseWithClaims(tokenString, &ClientData{}, func(token *jwt.Token) (interface{}, error) {
            return jwtSecret, nil
        })
        
        if err == nil && token.Valid {
            claims := token.Claims.(*ClientData)
            
            // 生成新随机值
            v := RandStringRunes(10)
            claims.Previous = append(claims.Previous, v)
            
            // 生成新令牌
            newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
            newTokenString, _ := newToken.SignedString(jwtSecret)
            context.Header("X-Auth-Token", newTokenString)
            
            context.JSON(http.StatusCreated, gin.H{
                "current":  v,
                "previous": claims.Previous,
            })
            return
        }
    }
    
    // 生成第一个随机值
    v := RandStringRunes(10)
    claims := &ClientData{
        Previous: []string{v},
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenString, _ = token.SignedString(jwtSecret)
    context.Header("X-Auth-Token", tokenString)
    
    context.JSON(http.StatusCreated, gin.H{
        "current":  v,
        "previous": []string{v},
    })
}

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}

func init() {
    rand.Seed(time.Now().UnixNano())
}

方案3:使用内存映射(基于客户端标识)

package textfiles

import (
    "github.com/gin-gonic/gin"
    "math/rand"
    "net/http"
    "sync"
    "time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
var clientData = make(map[string][]string)
var mu sync.RWMutex

func RandomTexts(context *gin.Context) {
    // 获取客户端标识(使用IP+User-Agent组合)
    clientIP := context.ClientIP()
    userAgent := context.GetHeader("User-Agent")
    clientID := clientIP + "|" + userAgent
    
    mu.Lock()
    defer mu.Unlock()
    
    // 获取或初始化客户端数据
    previous, exists := clientData[clientID]
    if !exists {
        previous = []string{}
    }
    
    // 生成新随机值
    v := RandStringRunes(10)
    previous = append(previous, v)
    
    // 保存数据
    clientData[clientID] = previous
    
    context.JSON(http.StatusCreated, gin.H{
        "current":  v,
        "previous": previous,
    })
}

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}

func init() {
    rand.Seed(time.Now().UnixNano())
}

推荐使用方案1(会话管理),因为它是最标准且可扩展的解决方案。方案2适合需要无状态API的场景,方案3适合简单的内存存储但重启服务器会丢失数据。

回到顶部