|
|
# 自动解析和自动编码实现方案
|
|
|
|
|
|
## 核心需求
|
|
|
|
|
|
1. **自动解析**:服务端接收到数据后,自动解析为header和struct,Handler直接使用
|
|
|
2. **自动编码**:Handler返回数据后,自动编码并发送
|
|
|
|
|
|
## 实现方案
|
|
|
|
|
|
### 1. 路由配置指定类型
|
|
|
|
|
|
在注册路由时指定请求和响应的类型:
|
|
|
|
|
|
```go
|
|
|
// 定义请求和响应类型
|
|
|
type LoginRequest struct {
|
|
|
Username string `json:"username"`
|
|
|
Password string `json:"password"`
|
|
|
}
|
|
|
|
|
|
type LoginResponse struct {
|
|
|
Code int `json:"code"`
|
|
|
Token string `json:"token"`
|
|
|
}
|
|
|
|
|
|
// 注册路由时指定类型
|
|
|
router.RegisterString(
|
|
|
"login",
|
|
|
loginHandler,
|
|
|
router.WithRequestType(&LoginRequest{}),
|
|
|
router.WithResponseType(&LoginResponse{}), // 可选
|
|
|
)
|
|
|
```
|
|
|
|
|
|
### 2. 服务器自动解析
|
|
|
|
|
|
在服务器接收到数据后,自动进行解析:
|
|
|
|
|
|
```go
|
|
|
// 服务器接收到数据
|
|
|
func (h *eventHandler) OnTraffic(c gnet.Conn) {
|
|
|
// 1. 读取原始数据
|
|
|
rawData, _ := c.Peek(-1)
|
|
|
|
|
|
// 2. 创建Context(包含Request和Response)
|
|
|
ctx := h.createContext(c, rawData)
|
|
|
|
|
|
// 3. 路由匹配(会返回路由配置,包含请求类型)
|
|
|
route, handler := h.router.Match(rawData, ctx)
|
|
|
if route == nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 4. 自动解析请求数据
|
|
|
if route.RequestType() != nil {
|
|
|
h.parseRequest(ctx, rawData, route)
|
|
|
}
|
|
|
|
|
|
// 5. 执行Handler(Handler中可以直接使用解析后的数据)
|
|
|
handler(ctx)
|
|
|
|
|
|
// 6. 自动发送响应(Response.Write()会自动编码)
|
|
|
ctx.Response().Send()
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 3. 自动解析逻辑
|
|
|
|
|
|
```go
|
|
|
func (h *eventHandler) parseRequest(ctx Context, rawData []byte, route Route) {
|
|
|
req := ctx.Request()
|
|
|
protocol := req.Protocol()
|
|
|
codec := h.getCodec(route)
|
|
|
|
|
|
// 1. 协议解码(提取帧头和数据)
|
|
|
header, bodyBytes, err := protocol.Decode(rawData)
|
|
|
if err != nil {
|
|
|
// 处理错误
|
|
|
return
|
|
|
}
|
|
|
req.SetHeader(header)
|
|
|
req.SetDataBytes(bodyBytes)
|
|
|
|
|
|
// 2. 数据解码(将字节解码为结构体)
|
|
|
if route.RequestType() != nil {
|
|
|
body := reflect.New(route.RequestType().Elem()).Interface()
|
|
|
if err := codec.Decode(bodyBytes, body); err != nil {
|
|
|
// 处理错误
|
|
|
return
|
|
|
}
|
|
|
req.SetData(body)
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 4. 自动编码逻辑
|
|
|
|
|
|
```go
|
|
|
func (resp *responseImpl) Write(data interface{}) error {
|
|
|
codec := resp.codec
|
|
|
protocol := resp.protocol
|
|
|
|
|
|
// 1. 编解码器编码(Go对象 → 字节)
|
|
|
bodyBytes, err := codec.Encode(data)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// 2. 协议编码(添加帧头)
|
|
|
header := resp.Header()
|
|
|
frameData, err := protocol.Encode(bodyBytes, header)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// 3. 写入连接
|
|
|
return resp.conn.Write(frameData)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## Handler使用示例
|
|
|
|
|
|
### 有帧头协议
|
|
|
|
|
|
```go
|
|
|
type LoginRequest struct {
|
|
|
Username string `json:"username"`
|
|
|
Password string `json:"password"`
|
|
|
}
|
|
|
|
|
|
type LoginResponse struct {
|
|
|
Code int `json:"code"`
|
|
|
Token string `json:"token"`
|
|
|
}
|
|
|
|
|
|
// 注册路由
|
|
|
router.RegisterString(
|
|
|
"login",
|
|
|
loginHandler,
|
|
|
router.WithRequestType(&LoginRequest{}),
|
|
|
)
|
|
|
|
|
|
// Handler实现(数据已自动解析)
|
|
|
func loginHandler(ctx Context) error {
|
|
|
req := ctx.Request()
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 直接使用解析后的数据
|
|
|
loginReq := req.Data().(*LoginRequest)
|
|
|
username := loginReq.Username
|
|
|
password := loginReq.Password
|
|
|
|
|
|
// 读取协议帧头(如果有)
|
|
|
header := req.Header()
|
|
|
if header != nil {
|
|
|
messageType := header.Get("message_type")
|
|
|
// 使用帧头信息
|
|
|
}
|
|
|
|
|
|
// 处理业务逻辑
|
|
|
token := authenticate(username, password)
|
|
|
|
|
|
// 设置响应帧头(如果有)
|
|
|
respHeader := resp.Header()
|
|
|
if respHeader != nil {
|
|
|
respHeader.Set("message_type", 0x02)
|
|
|
respHeader.Set("status", 0)
|
|
|
}
|
|
|
|
|
|
// 写入响应(自动编码)
|
|
|
return resp.Write(LoginResponse{
|
|
|
Code: 200,
|
|
|
Token: token,
|
|
|
})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 无帧头协议
|
|
|
|
|
|
```go
|
|
|
type EchoRequest struct {
|
|
|
Message string `json:"message"`
|
|
|
}
|
|
|
|
|
|
type EchoResponse struct {
|
|
|
Message string `json:"message"`
|
|
|
}
|
|
|
|
|
|
// 注册路由
|
|
|
router.RegisterString(
|
|
|
"echo",
|
|
|
echoHandler,
|
|
|
router.WithRequestType(&EchoRequest{}),
|
|
|
)
|
|
|
|
|
|
// Handler实现
|
|
|
func echoHandler(ctx Context) error {
|
|
|
req := ctx.Request()
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 直接使用解析后的数据(无帧头协议,header为nil)
|
|
|
echoReq := req.Data().(*EchoRequest)
|
|
|
|
|
|
// 写入响应(自动编码,协议会自动添加\n等分隔符)
|
|
|
return resp.Write(EchoResponse{
|
|
|
Message: echoReq.Message,
|
|
|
})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 路由接口更新
|
|
|
|
|
|
```go
|
|
|
// RouteOption 路由选项
|
|
|
type RouteOption func(*routeConfig)
|
|
|
|
|
|
// WithRequestType 指定请求类型
|
|
|
func WithRequestType(typ interface{}) RouteOption {
|
|
|
return func(cfg *routeConfig) {
|
|
|
cfg.requestType = reflect.TypeOf(typ)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// WithResponseType 指定响应类型(可选,主要用于验证)
|
|
|
func WithResponseType(typ interface{}) RouteOption {
|
|
|
return func(cfg *routeConfig) {
|
|
|
cfg.responseType = reflect.TypeOf(typ)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Router接口更新
|
|
|
type Router interface {
|
|
|
// Register 注册路由(支持选项)
|
|
|
Register(matcher Matcher, handler Handler, opts ...RouteOption) Route
|
|
|
|
|
|
// RegisterString 注册字符串路由(支持选项)
|
|
|
RegisterString(pattern string, handler Handler, opts ...RouteOption) Route
|
|
|
|
|
|
// ... 其他方法
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 数据流程
|
|
|
|
|
|
### 读数据(自动解析)
|
|
|
|
|
|
```
|
|
|
1. 数据到达 → Connection.Read()
|
|
|
↓
|
|
|
2. 创建Context和Request/Response
|
|
|
↓
|
|
|
3. 路由匹配 → 找到对应的路由和请求类型
|
|
|
↓
|
|
|
4. 协议解码 → Protocol.Decode() → Header + DataBytes
|
|
|
↓
|
|
|
5. 数据解码 → Codec.Decode() → Go Struct
|
|
|
↓
|
|
|
6. 设置到Request → Request.SetData(struct)
|
|
|
↓
|
|
|
7. Handler执行 → 直接使用req.Data().(*MyStruct)
|
|
|
```
|
|
|
|
|
|
### 写数据(自动编码)
|
|
|
|
|
|
```
|
|
|
1. Handler调用 → resp.Write(struct)
|
|
|
↓
|
|
|
2. 编解码器编码 → Codec.Encode() → []byte
|
|
|
↓
|
|
|
3. 协议编码 → Protocol.Encode() → Header + Data
|
|
|
↓
|
|
|
4. 写入连接 → Connection.Write()
|
|
|
```
|
|
|
|
|
|
## 实现步骤
|
|
|
|
|
|
1. ✅ 更新Request和Response接口
|
|
|
2. ⏳ 更新路由接口,支持指定请求/响应类型
|
|
|
3. ⏳ 实现自动解析逻辑
|
|
|
4. ⏳ 实现自动编码逻辑
|
|
|
5. ⏳ 更新服务器代码
|
|
|
6. ⏳ 更新协议实现
|
|
|
7. ⏳ 编写测试用例
|
|
|
|
|
|
## 优势
|
|
|
|
|
|
1. **自动化**:数据自动解析,无需手动调用Decode
|
|
|
2. **类型安全**:使用强类型,编译时检查
|
|
|
3. **简洁**:Handler代码简洁,直接使用结构体
|
|
|
4. **灵活**:支持有帧头和无帧头协议
|
|
|
5. **可配置**:通过路由配置指定类型
|
|
|
|