使用Golang构建带有REST API连接的聊天应用程序
使用Golang构建带有REST API连接的聊天应用程序 我开发了一个聊天应用程序。我正在寻找一个扩展算法。首先我有一个前端部分,是React应用。用户访问localhost/room/roomid,我会获取房间ID和存储在本地的token。然后我用token和房间ID触发websocket连接,这样我就可以将用户分配到不同的房间。这部分没问题。
但我的目标是:首先通过普通API获取最后10条消息,然后将其显示在屏幕上。接着正常通过socket接收新消息,使用append方法添加并保存到数据库中。请问这种做法是否正确?
// 代码示例
func main() {
fmt.Println("hello world")
}
更多关于使用Golang构建带有REST API连接的聊天应用程序的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这个问题与Go语言有什么关系?
更多关于使用Golang构建带有REST API连接的聊天应用程序的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你对Go服务器代码有什么问题吗?
使用Go编程语言开发我的服务器
要让ReactJS与Golang进行通信,你需要创建返回JSON的Golang API。那么,你是在寻找实现这个功能的代码吗?
这是一个非常合理的架构设计。你的方法遵循了现代实时聊天应用的最佳实践,我来详细解释并提供Go语言实现示例。
架构设计分析
你的方案是正确的:
- 初始加载:通过REST API获取历史消息,避免websocket连接建立期间的延迟
- 实时更新:通过WebSocket接收新消息并实时显示
- 数据持久化:所有消息都保存到数据库
Go语言实现示例
1. REST API端点(获取历史消息)
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
type Message struct {
ID string `json:"id"`
RoomID string `json:"roomId"`
UserID string `json:"userId"`
Content string `json:"content"`
Timestamp int64 `json:"timestamp"`
}
// 获取房间最后N条消息
func GetRoomMessages(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
roomID := vars["roomId"]
// 验证token和房间权限
token := r.Header.Get("Authorization")
if !validateToken(token, roomID) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// 从查询参数获取消息数量,默认10条
limitStr := r.URL.Query().Get("limit")
limit := 10
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
limit = parsedLimit
}
// 从数据库获取消息
messages, err := getMessagesFromDB(roomID, limit)
if err != nil {
http.Error(w, "Failed to fetch messages", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(messages)
}
func validateToken(token, roomID string) bool {
// 实现token验证逻辑
return true
}
func getMessagesFromDB(roomID string, limit int) ([]Message, error) {
// 实现数据库查询逻辑
// 示例返回假数据
return []Message{
{
ID: "1",
RoomID: roomID,
UserID: "user1",
Content: "Hello World",
Timestamp: 1633046400,
},
}, nil
}
2. WebSocket处理(实时消息)
package main
import (
"encoding/json"
"log"
"net/http"
"sync"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // 生产环境需要严格检查
},
}
type Client struct {
conn *websocket.Conn
roomID string
userID string
send chan []byte
}
type Room struct {
clients map[*Client]bool
broadcast chan []byte
register chan *Client
unregister chan *Client
mutex sync.RWMutex
}
var rooms = make(map[string]*Room)
func HandleWebSocket(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
roomID := vars["roomId"]
token := r.URL.Query().Get("token")
// 验证token
if !validateWebSocketToken(token, roomID) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("WebSocket upgrade failed: %v", err)
return
}
// 获取或创建房间
room := getOrCreateRoom(roomID)
client := &Client{
conn: conn,
roomID: roomID,
userID: extractUserIDFromToken(token),
send: make(chan []byte, 256),
}
room.register <- client
// 启动读写goroutine
go client.writePump()
go client.readPump()
}
func (c *Client) readPump() {
defer func() {
room := getOrCreateRoom(c.roomID)
room.unregister <- c
c.conn.Close()
}()
for {
_, message, err := c.conn.ReadMessage()
if err != nil {
break
}
// 处理接收到的消息
var msg Message
if err := json.Unmarshal(message, &msg); err != nil {
log.Printf("JSON unmarshal error: %v", err)
continue
}
// 保存到数据库
if err := saveMessageToDB(&msg); err != nil {
log.Printf("Save message error: %v", err)
continue
}
// 广播到房间
room := getOrCreateRoom(c.roomID)
room.broadcast <- message
}
}
func (c *Client) writePump() {
defer c.conn.Close()
for {
select {
case message, ok := <-c.send:
if !ok {
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
if err := c.conn.WriteMessage(websocket.TextMessage, message); err != nil {
return
}
}
}
}
func (r *Room) run() {
for {
select {
case client := <-r.register:
r.mutex.Lock()
r.clients[client] = true
r.mutex.Unlock()
case client := <-r.unregister:
r.mutex.Lock()
if _, ok := r.clients[client]; ok {
delete(r.clients, client)
close(client.send)
}
r.mutex.Unlock()
case message := <-r.broadcast:
r.mutex.RLock()
for client := range r.clients {
select {
case client.send <- message:
default:
close(client.send)
delete(r.clients, client)
}
}
r.mutex.RUnlock()
}
}
}
func getOrCreateRoom(roomID string) *Room {
if room, exists := rooms[roomID]; exists {
return room
}
room := &Room{
clients: make(map[*Client]bool),
broadcast: make(chan []byte),
register: make(chan *Client),
unregister: make(chan *Client),
}
rooms[roomID] = room
go room.run()
return room
}
func validateWebSocketToken(token, roomID string) bool {
// 实现WebSocket token验证
return true
}
func extractUserIDFromToken(token string) string {
// 从token中提取用户ID
return "user123"
}
func saveMessageToDB(message *Message) error {
// 实现消息保存到数据库的逻辑
log.Printf("Saving message to DB: %+v", message)
return nil
}
3. 主函数和路由设置
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
// REST API路由
r.HandleFunc("/api/rooms/{roomId}/messages", GetRoomMessages).Methods("GET")
// WebSocket路由
r.HandleFunc("/ws/rooms/{roomId}", HandleWebSocket)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}
前端集成示例
你的React前端应该这样调用:
// 1. 首先调用REST API获取历史消息
const fetchHistory = async (roomId, token) => {
const response = await fetch(`/api/rooms/${roomId}/messages?limit=10`, {
headers: {
'Authorization': token
}
});
return await response.json();
};
// 2. 然后建立WebSocket连接接收实时消息
const connectWebSocket = (roomId, token, onMessage) => {
const ws = new WebSocket(`ws://localhost:8080/ws/rooms/${roomId}?token=${token}`);
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
onMessage(message); // 使用append方法添加到UI
};
return ws;
};
这种架构确保了良好的用户体验:用户立即看到历史消息,同时实时接收新消息。所有消息都持久化到数据库,保证了数据完整性。

