# 自动解析和自动编码设计 ## 用户需求 1. **自动解析接收到的数据**: - 服务端在给定的协议下自动解析数据 - 自动提取header(如果有)和struct(数据部分) - Handler中能够直接使用解析后的结构数据 2. **自动编码写出的数据**: - 服务端写数据到客户端时能够自动编码 - Handler返回数据后自动进行编码和协议封装 ## 设计思路 ### 1. 自动解析流程 ``` 1. 数据到达 → Connection.Read() ↓ 2. 协议识别 → Protocol.Identify() (根据配置或数据特征) ↓ 3. 协议解码 → Protocol.Decode() → Header + Data ↓ 4. 编解码器识别 → Codec.Identify() (根据配置或数据特征) ↓ 5. 数据解码 → Codec.Decode() → Go Struct ↓ 6. 创建Request对象 → 包含Header和Data ↓ 7. 路由匹配 → 根据Header或Data匹配 ↓ 8. Handler执行 → 直接使用解析后的数据 ``` ### 2. 自动编码流程 ``` 1. Handler返回数据 → Response.Write(struct) ↓ 2. 编解码器编码 → Codec.Encode() → []byte ↓ 3. 协议编码 → Protocol.Encode() → Header + Data ↓ 4. 写入连接 → Connection.Write() ``` ## 新的设计 ### Request设计 ```go type Request interface { // Header 获取协议帧头(已解析) Header() FrameHeader // Data 获取数据部分(已解码为Go对象) Data() interface{} // Bind 绑定到指定的结构体(如果Data是map,可以绑定到struct) Bind(v interface{}) error // Raw 获取原始字节(如果需要) Raw() []byte } ``` ### Response设计 ```go type Response interface { // Write 写入数据(自动编码和协议封装) Write(data interface{}) error // WriteWithHeader 写入数据和协议帧头 WriteWithHeader(data interface{}, header FrameHeader) error // Header 获取/设置协议帧头 Header() FrameHeader SetHeader(header FrameHeader) } ``` ### Handler签名 ```go // 方式1:使用Context(推荐) type Handler func(ctx Context) error // 方式2:使用Request和Response(更明确) type Handler func(req Request, resp Response) error // 方式3:使用具体类型(最方便) type Handler func(header FrameHeader, body MyStruct, resp Response) error ``` ## 实现方案 ### 方案1:在服务器层面自动解析(推荐) 在服务器接收到数据后,立即进行解析: ```go // 服务器接收到数据 func (h *eventHandler) OnTraffic(c gnet.Conn) { // 1. 读取原始数据 data, _ := c.Peek(-1) // 2. 识别协议(根据配置或数据特征) protocol := h.identifyProtocol(data) // 3. 协议解码 header, bodyBytes, err := protocol.Decode(data) if err != nil { // 处理错误 return } // 4. 识别编解码器(根据配置或数据特征) codec := h.identifyCodec(bodyBytes) // 5. 数据解码(需要知道目标类型,可以从路由配置中获取) var body interface{} if targetType := h.getTargetType(header, bodyBytes); targetType != nil { body = reflect.New(targetType).Interface() codec.Decode(bodyBytes, body) } else { // 如果没有目标类型,保持为原始字节 body = bodyBytes } // 6. 创建Request对象 req := NewRequest(header, body, data) // 7. 创建Response对象 resp := NewResponse(protocol, codec, c) // 8. 创建Context ctx := NewContext(req, resp, c) // 9. 路由匹配和执行 handler := h.router.Match(ctx) handler(ctx) } ``` ### 方案2:延迟解析(按需解析) 在Handler中按需解析: ```go // Request对象延迟解析 type Request struct { rawData []byte protocol Protocol codec Codec header FrameHeader body interface{} parsed bool } func (r *Request) Data() interface{} { if !r.parsed { r.parse() } return r.data } func (r *Request) parse() { // 协议解码 r.header, bodyBytes, _ := r.protocol.Decode(r.rawData) // 数据解码(需要目标类型) r.data = r.decodeBody(bodyBytes) r.parsed = true } ``` ### 方案3:基于路由配置的自动解析(最佳) 在路由注册时指定目标类型,服务器自动解析: ```go // 注册路由时指定类型 type UserRequest struct { UserID int `json:"user_id"` } type UserResponse struct { Code int `json:"code"` Data string `json:"data"` } // 注册路由 router.Register( matcher, handler, WithRequestType(&UserRequest{}), // 指定请求类型 WithResponseType(&UserResponse{}), // 指定响应类型(可选) ) // Handler签名 func handler(ctx Context) error { req := ctx.Request() // 直接使用解析后的数据 userReq := req.Data().(*UserRequest) userID := userReq.UserID // 返回响应(自动编码) return ctx.Response().Write(UserResponse{ Code: 200, Data: "success", }) } ``` ## 推荐实现:方案3(基于路由配置) ### 路由注册 ```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) // 处理业务逻辑 token := authenticate(loginReq.Username, loginReq.Password) // 返回响应(自动编码) return resp.Write(LoginResponse{ Code: 200, Token: token, }) } ``` ### 服务器实现 ```go // 在路由匹配时自动解析 func (r *router) Match(input MatchInput, ctx Context) (Handler, error) { // 1. 匹配路由 route := r.findRoute(data, ctx) if route == nil { return nil, ErrRouteNotFound } // 2. 如果路由指定了请求类型,自动解析 if route.RequestType() != nil { req := ctx.Request() // 协议解码 header, bodyBytes, err := req.Protocol().Decode(data) if err != nil { return nil, err } req.SetHeader(header) // 数据解码 body := reflect.New(route.RequestType()).Interface() codec := req.Codec() if err := codec.Decode(bodyBytes, body); err != nil { return nil, err } req.SetData(body) } return route.Handler(), nil } ``` ## 完整的数据流程 ### 读数据(自动解析) ``` 1. 数据到达 → Connection.Read() ↓ 2. 创建Context和Request/Response ↓ 3. 路由匹配 → 找到对应的路由 ↓ 4. 检查路由配置 → 是否有RequestType ↓ 5. 协议解码 → Protocol.Decode() → Header + DataBytes ↓ 6. 数据解码 → Codec.Decode() → Go Struct ↓ 7. 设置到Request → Request.SetData(struct) ↓ 8. Handler执行 → 直接使用req.Data().(*MyStruct) ``` ### 写数据(自动编码) ``` 1. Handler调用 → resp.Write(struct) ↓ 2. 编解码器编码 → Codec.Encode() → []byte ↓ 3. 协议编码 → Protocol.Encode() → Header + Data ↓ 4. 写入连接 → Connection.Write() ``` ## 优势 1. **自动化**:无需手动解析,数据自动解析为结构体 2. **类型安全**:使用强类型,编译时检查 3. **简洁**:Handler代码简洁,直接使用结构体 4. **灵活**:支持有帧头和无帧头协议 5. **可配置**:通过路由配置指定类型 ## 实现步骤 1. 更新路由接口,支持指定请求/响应类型 2. 实现自动解析逻辑 3. 更新Request和Response接口 4. 更新服务器代码 5. 编写测试用例