# Transport adapters 业务 Handler 依赖 GoOmni 的 `httpx.Context`,Service 接收标准 `context.Context`。适配器只在启动入口组装: ```go import ( omni "git.yuntuo.hk/k2safe/go-omni" netrouter "git.yuntuo.hk/k2safe/go-omni/transport/nethttp" ) application := omni.NewWithRouter(netrouter.New()) registerRoutes(application) // 与 omni.New() 复用同一份业务注册代码 ``` `transport/gin` 默认使用 Gin 分发请求;`transport/nethttp` 使用独立的标准库路由实现。两者共用路由声明校验和路径匹配规则,Gin 的正常请求仍由 Gin 路由树处理;它的 404/405 回退由适配层统一处理。 ## 可移植路由规则(v0.3.0) | 项目 | 约定 | | --- | --- | | 路径 | 顶层路由必须以 `/` 开头;区分大小写。禁止重复斜杠、`.` / `..` 段、查询串和片段标识。 | | 参数 | `/users/:id` 匹配一个非空路径段;参数必须占据整个段,同一路由内名称唯一。 | | 通配 | `/files/*path` 仅允许出现在末尾,值包含开头的 `/`;`/files/` 对应 `/`,`/files` 不匹配。 | | 路由优先级 | 相同方法内,从左向右比较,静态段优先于参数段;更具体的分支匹配失败后允许回退。 | | 注册冲突 | 同方法重复注册、相同前缀处参数名冲突、catch-all 与同级分支冲突,均在注册时 panic。不同方法可用同一路径。 | | 分组 | 支持嵌套与相对路径。`Group("/api").Handle("GET", "", h)` 注册 `/api`,传 `"/"` 则注册 `/api/`。 | | 尾部斜杠 | `/users` 与 `/users/` 是不同路由;不自动重定向、补斜杠或清理请求路径。 | | URL 编码 | 使用已解码的 `URL.Path`;例如 `%2F` 作为路径分隔符,`+` 保留为加号。 | | 方法 | `Handle` 接受大写字母组成的方法名;HEAD 不自动复用 GET,OPTIONS 不自动成功。 | | Any | 注册 GET、POST、PUT、PATCH、HEAD、OPTIONS、DELETE、CONNECT、TRACE 九种方法;其他方法需显式注册。 | | 404 / 405 | 未找到路径返回纯文本 404;路径存在但方法不支持时返回纯文本 405,`Allow` 去重并按字母排序。 | | 并发 | 启动前完成路由注册;允许并发处理请求,各请求参数、业务值、requestId 必须隔离。 | HTTP 回退响应属于路由层,不进入应用路由中间件,不携带统一 JSON 封装或 requestId。业务 Handler 返回的错误则经过统一 JSON 错误处理。 业务层通过 `application.Handle("HEAD", path, handler)` 或 `group.Handle("OPTIONS", path, handler)` 显式注册其他方法,同样经过框架中间件和错误处理。 `gin.NewWithEngine` 保留原生路由和中间件,但覆盖重定向、路径清理和 404/405 策略。可移植契约与 `Allow` 集合只覆盖经 GoOmni Router 注册的路由;直接操作 `Engine()` 的原生扩展不属于该保证。 ## 业务行为 - 同一套 Handler、Service、中间件用于两种适配器;应用、分组、嵌套分组、路由中间件依次进入、反序退出。 - `Use` 只影响后续注册的路由。鉴权失败短路处理链,panic 转为统一 500。 - URI / Query / JSON 绑定、业务错误、requestId 与业务日志关联一致。 - `JSON` / `OK` 等方法先编码再发送;编码失败返回统一 500。已通过 Context 提交响应后,后续错误、panic 或重复响应不会追加 JSON。 - 客户端断开会取消传入 Service 的 context;适配时保留原有 context 的值、截止时间和取消信号。 - `Raw()` 是高级逃生口;直接写入、流式响应、Hijack 等行为不属于上述 Context 响应追踪契约,勿与自动响应方法混用。 ## 自研适配器验收 实现 `httpx.Router` / `httpx.RouterGroup`,在调用 Handler 前通过 `httpx.WithRouteParams` 和 `httpx.WithRoutePattern` 注入请求元数据。使用外部测试包接入公共验收套件: ```go package myrouter_test import ( "testing" "git.yuntuo.hk/k2safe/go-omni/httpx" "git.yuntuo.hk/k2safe/go-omni/transport/transporttest" "example.com/project/myrouter" ) func TestContract(t *testing.T) { transporttest.Run(t, func() httpx.Router { return myrouter.New() }) } ``` 工厂需每次返回新的空路由器。套件依次创建路由器,内部会发送并发请求、打开本地 HTTP 测试服务,运行时应启用 race 检查: ```sh go test -race ./transport/... ./examples/reference python3 scripts/verifyTransports.py --version v0.3.0 ``` 第一条同时验证完整用户 CRUD 在两种适配器上复用相同 Handler / Service / Repository。第二条在源码目录外创建无 replace 的消费模块,从远程版本导入同一套验收包。 当前替换边界是路由与 HTTP 适配层,`App.Run` 仍使用标准库 `net/http.Server`。未来自研监听、连接管理或协议解析时,可让自研服务调用 `App.ServeHTTP`(提供标准 HTTP 请求/响应桥接),由自研启动层管理监听与关闭,并在退出时调用 `App.Close`;本阶段没有实现新的网络内核。