diff --git a/devcloud/mcenter/apps/token/api/api.go b/devcloud/mcenter/apps/token/api/api.go index 778f64e..f4abc85 100644 --- a/devcloud/mcenter/apps/token/api/api.go +++ b/devcloud/mcenter/apps/token/api/api.go @@ -1 +1,66 @@ package api + +import ( + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token" + "github.com/infraboard/mcube/v2/ioc" + "github.com/infraboard/mcube/v2/ioc/config/gorestful" + + restfulspec "github.com/emicklei/go-restful-openapi/v2" +) + +func init() { + ioc.Api().Registry(&TokenRestulApiHandler{}) +} + +type TokenRestulApiHandler struct { + ioc.ObjectImpl + + // 依赖控制器 + svc token.Service +} + +func (h *TokenRestulApiHandler) Name() string { + return token.APP_NAME +} + +func (h *TokenRestulApiHandler) Init() error { + h.svc = token.GetService() + + tags := []string{"用户登录"} + ws := gorestful.ObjectRouter(h) + ws.Route(ws.POST("").To(h.Login). + Doc("颁发令牌(登录)"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Reads(token.IssueTokenRequest{}). + Writes(token.Token{}). + Returns(200, "OK", token.Token{})) + + ws.Route(ws.POST("/validate").To(h.ValiateToken). + Doc("校验令牌"). + // Metadata(permission.Auth(true)). + // Metadata(permission.Permission(false)). + Metadata(restfulspec.KeyOpenAPITags, tags). + Reads(token.ValiateTokenRequest{}). + Writes(token.Token{}). + Returns(200, "OK", token.Token{})) + + // ws.Route(ws.POST("/change_namespace").To(h.ChangeNamespce). + // Doc("切换令牌访问空间"). + // // Metadata(permission.Auth(true)). + // // Metadata(permission.Permission(false)). + // Metadata(restfulspec.KeyOpenAPITags, tags). + // Reads(token.ChangeNamespceRequest{}). + // Writes(token.Token{}). + // Returns(200, "OK", token.Token{})) + + ws.Route(ws.DELETE("").To(h.Logout). + Doc("撤销令牌(退出)"). + // Metadata(permission.Auth(true)). + // Metadata(permission.Permission(false)). + Metadata(restfulspec.KeyOpenAPITags, tags). + Reads(token.IssueTokenRequest{}). + Writes(token.Token{}). + Returns(200, "OK", token.Token{}). + Returns(404, "Not Found", nil)) + return nil +} diff --git a/devcloud/mcenter/apps/token/api/token.go b/devcloud/mcenter/apps/token/api/token.go index 778f64e..0dc60aa 100644 --- a/devcloud/mcenter/apps/token/api/token.go +++ b/devcloud/mcenter/apps/token/api/token.go @@ -1 +1,127 @@ package api + +import ( + "net/http" + "net/url" + + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token" + "github.com/emicklei/go-restful/v3" + "github.com/infraboard/mcube/v2/http/restful/response" + "github.com/infraboard/mcube/v2/ioc/config/application" +) + +func (h *TokenRestulApiHandler) Login(r *restful.Request, w *restful.Response) { + // 1. 获取用户的请求参数, 参数在Body里面 + req := token.NewIssueTokenRequest() + + // 获取用户通过body传入的参数 + err := r.ReadEntity(req) + if err != nil { + response.Failed(w, err) + return + } + + // 设置当前调用者的Token + // Private 用户自己的Token + // 如果你是user/password 这种方式,token 直接放到body + switch req.Issuer { + case token.ISSUER_PRIVATE_TOKEN: + req.Parameter.SetAccessToken(token.GetAccessTokenFromHTTP(r.Request)) + } + + // 2. 执行逻辑 + tk, err := h.svc.IssueToken(r.Request.Context(), req) + if err != nil { + response.Failed(w, err) + return + } + + // access_token 通过SetCookie 直接写到浏览器客户端(Web) + http.SetCookie(w, &http.Cookie{ + Name: token.ACCESS_TOKEN_COOKIE_NAME, + Value: url.QueryEscape(tk.AccessToken), + MaxAge: 0, + Path: "/", + Domain: application.Get().Domain(), + SameSite: http.SameSiteDefaultMode, + Secure: false, + HttpOnly: true, + }) + // 在Header头中也添加Token + w.Header().Set(token.ACCESS_TOKEN_RESPONSE_HEADER_NAME, tk.AccessToken) + + // 3. Body中返回Token对象 + response.Success(w, tk) +} + +// func (h *TokenRestulApiHandler) ChangeNamespce(r *restful.Request, w *restful.Response) { +// // 1. 获取用户的请求参数, 参数在Body里面 +// req := token.NewChangeNamespceRequest() +// err := r.ReadEntity(req) +// if err != nil { +// response.Failed(w, err) +// return +// } + +// tk := token.GetTokenFromCtx(r.Request.Context()) +// req.UserId = tk.UserId + +// // 2. 执行逻辑 +// tk, err = h.svc.ChangeNamespce(r.Request.Context(), req) +// if err != nil { +// response.Failed(w, err) +// return +// } + +// // 3. Body中返回Token对象 +// response.Success(w, tk) +// } + +// Logout HandleFunc +func (h *TokenRestulApiHandler) Logout(r *restful.Request, w *restful.Response) { + req := token.NewRevolkTokenRequest( + token.GetAccessTokenFromHTTP(r.Request), + token.GetRefreshTokenFromHTTP(r.Request), + ) + + tk, err := h.svc.RevolkToken(r.Request.Context(), req) + if err != nil { + response.Failed(w, err) + return + } + + // access_token 通过SetCookie 直接写到浏览器客户端(Web) + http.SetCookie(w, &http.Cookie{ + Name: token.ACCESS_TOKEN_COOKIE_NAME, + Value: "", + MaxAge: 0, + Path: "/", + Domain: application.Get().Domain(), + SameSite: http.SameSiteDefaultMode, + Secure: false, + HttpOnly: true, + }) + + // 3. 返回响应 + response.Success(w, tk) +} + +func (h *TokenRestulApiHandler) ValiateToken(r *restful.Request, w *restful.Response) { + // 1. 获取用户的请求参数, 参数在Body里面 + req := token.NewValiateTokenRequest("") + err := r.ReadEntity(req) + if err != nil { + response.Failed(w, err) + return + } + + // 2. 执行逻辑 + tk, err := h.svc.ValiateToken(r.Request.Context(), req) + if err != nil { + response.Failed(w, err) + return + } + + // 3. Body中返回Token对象 + response.Success(w, tk) +} diff --git a/go.mod b/go.mod index f62d8ec..e7ed942 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,14 @@ go 1.24.1 require ( github.com/caarlos0/env/v6 v6.10.1 + github.com/emicklei/go-restful-openapi/v2 v2.11.0 + github.com/emicklei/go-restful/v3 v3.12.2 github.com/gin-gonic/gin v1.10.0 github.com/infraboard/mcube/v2 v2.0.59 github.com/infraboard/modules v0.0.12 github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.10.0 golang.org/x/crypto v0.38.0 - golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.7 gorm.io/gorm v1.26.0 @@ -76,6 +77,7 @@ require ( github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.2 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect diff --git a/go.sum b/go.sum index 18946bd..d85d4db 100644 --- a/go.sum +++ b/go.sum @@ -18,11 +18,17 @@ github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/emicklei/go-restful-openapi/v2 v2.11.0 h1:Ur+yGxoOH/7KRmcj/UoMFqC3VeNc9VOe+/XidumxTvk= +github.com/emicklei/go-restful-openapi/v2 v2.11.0/go.mod h1:4CTuOXHFg3jkvCpnXN+Wkw5prVUnP8hIACssJTYorWo= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= @@ -38,12 +44,18 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -99,14 +111,20 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -124,6 +142,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -156,6 +175,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -173,10 +193,14 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/Oa github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.60.0 h1:jvVaMcOzU4abk6o8VCJJN5wRoFQxe3Afnc8fPcYbO0I= +go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.60.0/go.mod h1:NMt0e4UA3rY+CV+xyjE4wLhz/6BiydSOZhMeevBDZB0= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.60.0 h1:jj/B7eX95/mOxim9g9laNZkOHKz/XCHG0G410SntRy4= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.60.0/go.mod h1:ZvRTVaYYGypytG0zRp2A60lpj//cMq3ZnxYdZaljVBM= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= +go.opentelemetry.io/contrib/propagators/b3 v1.35.0 h1:DpwKW04LkdFRFCIgM3sqwTJA/QREHMeMHYPWP1WeaPQ= +go.opentelemetry.io/contrib/propagators/b3 v1.35.0/go.mod h1:9+SNxwqvCWo1qQwUpACBY5YKNVxFJn5mlbXg/4+uKBg= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= @@ -199,8 +223,6 @@ golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= @@ -221,11 +243,16 @@ google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3i google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= diff --git a/skills/gorestful/README.md b/skills/gorestful/README.md new file mode 100644 index 0000000..214d96f --- /dev/null +++ b/skills/gorestful/README.md @@ -0,0 +1,181 @@ +# gorestful Web框架 + +[go-restful](https://github.com/emicklei/go-restful) + +```go +package main + +import ( + "fmt" + "log" + "net/http" + "time" + + restfulspec "github.com/emicklei/go-restful-openapi/v2" + restful "github.com/emicklei/go-restful/v3" + "github.com/go-openapi/spec" +) + +// UserResource is the REST layer to the User domain +type UserResource struct { + // normally one would use DAO (data access object) + users map[string]User +} + +// WebService creates a new service that can handle REST requests for User resources. +func (u UserResource) WebService() *restful.WebService { + ws := new(restful.WebService) + ws. + Path("/users"). + Consumes(restful.MIME_XML, restful.MIME_JSON). + Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well + + tags := []string{"users"} + + ws.Route(ws.GET("/").To(u.findAllUsers). + // docs + Doc("get all users"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Writes([]User{}). + Returns(200, "OK", []User{})) + + ws.Route(ws.GET("/{user-id}").To(u.findUser). + // docs + Doc("get a user"). + Param(ws.PathParameter("user-id", "identifier of the user").DataType("integer").DefaultValue("1")). + Metadata(restfulspec.KeyOpenAPITags, tags). + Writes(User{}). // on the response + Returns(200, "OK", User{}). + Returns(404, "Not Found", nil)) + + ws.Route(ws.PUT("/{user-id}").To(u.upsertUser). + // docs + Doc("update a user"). + Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). + Metadata(restfulspec.KeyOpenAPITags, tags). + Reads(User{})) // from the request + + ws.Route(ws.POST("").To(u.createUser). + // docs + Doc("create a user"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Reads(User{})) // from the request + + ws.Route(ws.DELETE("/{user-id}").To(u.removeUser). + // docs + Doc("delete a user"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Param(ws.PathParameter("user-id", "identifier of the user").DataType("string"))) + + return ws +} + +// GET http://localhost:8080/users +func (u UserResource) findAllUsers(request *restful.Request, response *restful.Response) { + log.Println("findAllUsers") + list := []User{} + for _, each := range u.users { + list = append(list, each) + } + response.WriteEntity(list) +} + +// GET http://localhost:8080/users/1 +func (u UserResource) findUser(request *restful.Request, response *restful.Response) { + log.Println("findUser") + id := request.PathParameter("user-id") + usr := u.users[id] + if len(usr.ID) == 0 { + response.WriteErrorString(http.StatusNotFound, "User could not be found.") + } else { + response.WriteEntity(usr) + } +} + +// PUT http://localhost:8080/users/1 +// 1Melissa Raspberry +func (u *UserResource) upsertUser(request *restful.Request, response *restful.Response) { + log.Println("upsertUser") + usr := User{ID: request.PathParameter("user-id")} + err := request.ReadEntity(&usr) + if err == nil { + u.users[usr.ID] = usr + response.WriteEntity(usr) + } else { + response.WriteError(http.StatusInternalServerError, err) + } +} + +// POST http://localhost:8080/users +// 1Melissa +func (u *UserResource) createUser(request *restful.Request, response *restful.Response) { + log.Println("createUser") + usr := User{ID: fmt.Sprintf("%d", time.Now().Unix())} + err := request.ReadEntity(&usr) + if err == nil { + u.users[usr.ID] = usr + response.WriteHeaderAndEntity(http.StatusCreated, usr) + } else { + response.WriteError(http.StatusInternalServerError, err) + } +} + +// DELETE http://localhost:8080/users/1 +func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) { + log.Println("removeUser") + id := request.PathParameter("user-id") + delete(u.users, id) +} + +func main() { + u := UserResource{map[string]User{}} + restful.DefaultContainer.Add(u.WebService()) + + config := restfulspec.Config{ + WebServices: restful.RegisteredWebServices(), // you control what services are visible + APIPath: "/apidocs.json", + PostBuildSwaggerObjectHandler: enrichSwaggerObject} + restful.DefaultContainer.Add(restfulspec.NewOpenAPIService(config)) + + // Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API + // You need to download the Swagger HTML5 assets and change the FilePath location in the config below. + // Open http://localhost:8080/apidocs/?url=http://localhost:8080/apidocs.json + http.Handle("/apidocs/", http.StripPrefix("/apidocs/", http.FileServer(http.Dir("/Users/emicklei/Projects/swagger-ui/dist")))) + + log.Printf("start listening on localhost:8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func enrichSwaggerObject(swo *spec.Swagger) { + swo.Info = &spec.Info{ + InfoProps: spec.InfoProps{ + Title: "UserService", + Description: "Resource for managing Users", + Contact: &spec.ContactInfo{ + ContactInfoProps: spec.ContactInfoProps{ + Name: "john", + Email: "john@doe.rp", + URL: "http://johndoe.org", + }, + }, + License: &spec.License{ + LicenseProps: spec.LicenseProps{ + Name: "MIT", + URL: "http://mit.org", + }, + }, + Version: "1.0.0", + }, + } + swo.Tags = []spec.Tag{spec.Tag{TagProps: spec.TagProps{ + Name: "users", + Description: "Managing users"}}} +} + +// User is just a sample type +type User struct { + ID string `xml:"id" json:"id" description:"identifier of the user"` + Name string `xml:"name" json:"name" description:"name of the user" default:"john"` + Age int `xml:"age" json:"age" description:"age of the user" default:"21"` +} +``` \ No newline at end of file diff --git a/skills/gorestful/framework.drawio b/skills/gorestful/framework.drawio new file mode 100644 index 0000000..f2c05a6 --- /dev/null +++ b/skills/gorestful/framework.drawio @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file