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
非常感谢。按照您的建议,它成功运行了。
更多关于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开发中通常被称为“会话”或类似功能的东西。通常,从高层次来看,其流程大致如下:
- 客户端(浏览器)以某种方式向服务器标识自己(通常通过身份验证)。
- 服务器向客户端发送某种会话标识符(通常是JWT或某种生成的密钥/GUID)。
- 客户端将标识符存储在某个地方(通常在Cookie或本地存储中)。
- 在后续请求中,客户端发送该标识符(通常作为请求头),以便服务器知道该请求属于哪个会话。
问题在于使用了全局变量 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适合简单的内存存储但重启服务器会丢失数据。

