feat: remove Gin and standardize on nethttp

This commit is contained in:
2026-09-21 00:57:06 +08:00
parent 61c37e0dba
commit 3bbf4627a1
34 changed files with 197 additions and 305 deletions

View File

@@ -41,8 +41,8 @@ examples/reference/
调用路径:HTTP → Handler → Service → Repository。
- Handler 依赖 GoOmni,示例代码不导入 Gin,也不调用 `Engine()` / `Raw()`。
- Service、Repository、Model 不依赖 GoOmni 或 Gin,通过标准 `context.Context` 传递请求取消信号。
- Handler 依赖 GoOmni,示例代码不导入具体网络库,也不调用 `Engine()` / `Raw()`。
- Service、Repository、Model 不依赖 GoOmni 或网络库,通过标准 `context.Context` 传递请求取消信号。
- JSON 校验标签留在 Handler 的请求结构上;业务模型没有 HTTP 绑定标签。
- Service 返回业务错误,由 Handler 映射为 HTTP 错误;未知错误交给框架转换成通用 500 响应。
- 通过 `application.Group("/api/v1", handler.RequestLog(logger))` 挂载自定义中间件,再注册 `/users` 子路由。日志记录请求 ID、耗时和返回错误;panic 由框架恢复中间件处理。
@@ -156,6 +156,6 @@ go vet ./...
go build ./...
```
`TestUserCRUD` 将同一份业务注册代码分别挂载到 Gin 与 nethttp,验证替换适配器后 Handler、Service、Repository 均无需修改。
`TestUserCRUD` 使用标准库适配器运行完整业务注册代码;同一套 Handler、Service、Repository 也可通过 `omni.NewWithRouter` 接入自研适配器。
测试覆盖完整 CRUD、认证和角色权限、校验失败、分页边界、邮箱冲突及失败更新、删除后重用邮箱、请求 ID、日志关联、内部错误隐藏、并发唯一性、取消请求不修改数据、数据库配置解析和 HTTP 优雅退出。MySQL 测试不自动连接外部数据库。

View File

@@ -20,7 +20,6 @@ import (
"git.yuntuo.hk/k2safe/go-omni/examples/reference/repository"
"git.yuntuo.hk/k2safe/go-omni/httpx"
"git.yuntuo.hk/k2safe/go-omni/logx"
ginrouter "git.yuntuo.hk/k2safe/go-omni/transport/gin"
netrouter "git.yuntuo.hk/k2safe/go-omni/transport/nethttp"
)
@@ -32,7 +31,7 @@ type envelope struct {
}
func testApp() *omni.App {
return newApp(repository.NewMemoryUsers(), testLogger(io.Discard), omni.WithMode("test"))
return newApp(repository.NewMemoryUsers(), testLogger(io.Discard))
}
func testLogger(writer io.Writer) *logx.Logger {
@@ -87,7 +86,6 @@ func TestProtectedWriteRoutes(t *testing.T) {
repository.NewMemoryUsers(),
testLogger(io.Discard),
auth.StaticAuthenticator{"user-token": {UserID: "user-1", Roles: []string{"user"}}},
omni.WithMode("test"),
)
request(t, app, "POST", "/api/v1/users", `{"name":"Alice","email":"alice@example.com"}`, 401, "UNAUTHENTICATED")
created := dataAs[model.User](t, requestWithAuthorization(t, app, "POST", "/api/v1/users", `{"name":"Alice","email":"alice@example.com"}`, "Bearer user-token", 201, "CREATED"))
@@ -110,7 +108,6 @@ func TestUserCRUD(t *testing.T) {
name string
newRouter func() httpx.Router
}{
{"gin", func() httpx.Router { return ginrouter.New("test") }},
{"nethttp", func() httpx.Router { return netrouter.New() }},
} {
t.Run(adapter.name, func(t *testing.T) {
@@ -226,7 +223,7 @@ func TestErrorTranslationAndRequestLogging(t *testing.T) {
} {
t.Run(test.name, func(t *testing.T) {
var logs bytes.Buffer
app := newApp(failingUsers{repository.NewMemoryUsers(), test.err}, testLogger(&logs), omni.WithMode("test"))
app := newApp(failingUsers{repository.NewMemoryUsers(), test.err}, testLogger(&logs))
result := request(t, app, "GET", "/api/v1/users/1", "", test.status, test.code)
if strings.Contains(result.Message, test.err.Error()) || len(result.Data) != 0 {
t.Fatalf("internal error details leaked: %+v", result)