feat(service): 具名化 JS 导出并动态包裹错误(by AI)
This commit is contained in:
parent
61d56bfddc
commit
d67dd6d43b
17
CHANGELOG.md
17
CHANGELOG.md
@ -1,5 +1,22 @@
|
|||||||
# CHANGELOG - go/service
|
# CHANGELOG - go/service
|
||||||
|
|
||||||
|
## v1.5.15 (2026-06-21)
|
||||||
|
- **错误堆栈重构**:
|
||||||
|
- 重构 `js_export.go`,将匿名占位工厂函数改写为包级具名函数。
|
||||||
|
- 使用 `jsmod.MakeError` 包裹 `Upgrade` 返回的错误,并在 `Response`、`Session`、`WebSocketConn` 的相关出错方法中包裹返回的错误。
|
||||||
|
- **修复**:
|
||||||
|
- 修复 `RouteHandler.ServeHTTP` 在 `RouteHandler.ws` 为空时可能触发的 nil 指针引用 panic,使其安全降级使用 `DefaultServer`。
|
||||||
|
- **依赖对齐**:
|
||||||
|
- 升级依赖 `jsmod` 至 `v1.5.3`, `timer` (`v1.5.0`), `cast` 至 `v1.5.3`, `rand` 至 `v1.5.3`, `encoding` 至 `v1.5.4`, `shell` 至 `v1.5.3`, `safe` 至 `v1.5.2`, `id` 至 `v1.5.4`, `crypto` 至 `v1.5.3`, `file` 至 `v1.5.5`, `config` 至 `v1.5.3`, `log` 至 `v1.5.8`, `http` 至 `v1.5.3`, `redis` 至 `v1.5.6`, `starter` 至 `v1.5.5`, `discover` 至 `v1.5.3`。
|
||||||
|
|
||||||
|
## v1.5.14 (2026-06-11)
|
||||||
|
- **依赖对齐**:
|
||||||
|
- 对齐基础设施依赖到 v1.5.x。
|
||||||
|
|
||||||
|
## v1.5.13 (2026-06-10)
|
||||||
|
- **优化**:
|
||||||
|
- 对齐 JS 导出为 PascalCase 格式。
|
||||||
|
|
||||||
## v1.5.12 (2026-06-07)
|
## v1.5.12 (2026-06-07)
|
||||||
- **基础设施对齐: 切换至 starter v1.5.3 编排模式**:
|
- **基础设施对齐: 切换至 starter v1.5.3 编排模式**:
|
||||||
- 弃用已废弃的 `starter.Run()`,全面转向 `starter.Start() / starter.Wait()`。
|
- 弃用已废弃的 `starter.Run()`,全面转向 `starter.Start() / starter.Wait()`。
|
||||||
|
|||||||
2
TEST.md
2
TEST.md
@ -1,7 +1,7 @@
|
|||||||
# Service Module Test Report
|
# Service Module Test Report
|
||||||
|
|
||||||
## 性能测试 (Benchmark)
|
## 性能测试 (Benchmark)
|
||||||
- 测试日期: 2026-05-10
|
- 测试日期: 2026-06-21
|
||||||
- 版本: v1.0.4
|
- 版本: v1.0.4
|
||||||
- 指标: `BenchmarkRouting`: 2791 ns/op
|
- 指标: `BenchmarkRouting`: 2791 ns/op
|
||||||
- 环境: Darwin / Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
|
- 环境: Darwin / Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
|
||||||
|
|||||||
33
go.mod
33
go.mod
@ -3,27 +3,30 @@ module apigo.cc/go/service
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/cast v1.5.2
|
apigo.cc/go/cast v1.5.3
|
||||||
apigo.cc/go/config v1.5.2
|
apigo.cc/go/config v1.5.3
|
||||||
apigo.cc/go/discover v1.5.2
|
apigo.cc/go/discover v1.5.3
|
||||||
apigo.cc/go/file v1.5.4
|
apigo.cc/go/file v1.5.5
|
||||||
apigo.cc/go/http v1.5.2
|
apigo.cc/go/http v1.5.3
|
||||||
apigo.cc/go/id v1.5.3
|
apigo.cc/go/id v1.5.4
|
||||||
apigo.cc/go/jsmod v1.5.2
|
apigo.cc/go/jsmod v1.5.3
|
||||||
apigo.cc/go/log v1.5.6
|
apigo.cc/go/log v1.5.8
|
||||||
apigo.cc/go/redis v1.5.4
|
apigo.cc/go/redis v1.5.6
|
||||||
apigo.cc/go/safe v1.5.1
|
apigo.cc/go/safe v1.5.2
|
||||||
apigo.cc/go/starter v1.5.4
|
apigo.cc/go/starter v1.5.5
|
||||||
apigo.cc/go/timer v1.5.0
|
apigo.cc/go/timer v1.5.0
|
||||||
|
apigo.cc/go/watch v1.5.2
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
golang.org/x/net v0.54.0
|
golang.org/x/net v0.54.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/crypto v1.5.2 // indirect
|
apigo.cc/go/crypto v1.5.3 // indirect
|
||||||
apigo.cc/go/encoding v1.5.3 // indirect
|
apigo.cc/go/encoding v1.5.4 // indirect
|
||||||
apigo.cc/go/rand v1.5.2 // indirect
|
apigo.cc/go/rand v1.5.3 // indirect
|
||||||
apigo.cc/go/shell v1.5.2 // indirect
|
apigo.cc/go/shell v1.5.3 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||||
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||||
golang.org/x/crypto v0.52.0 // indirect
|
golang.org/x/crypto v0.52.0 // indirect
|
||||||
golang.org/x/sys v0.45.0 // indirect
|
golang.org/x/sys v0.45.0 // indirect
|
||||||
|
|||||||
64
go.sum
64
go.sum
@ -1,33 +1,41 @@
|
|||||||
apigo.cc/go/cast v1.5.0 h1:UBGJtFQ8eJPMQXs37cUgqd7YQo1zI9opuSDBDmn2/pE=
|
apigo.cc/go/cast v1.5.3 h1:jk6VX0rGFhjKtfPhsaV6IKYpiGmORRk9qPTtuNS53tw=
|
||||||
apigo.cc/go/cast v1.5.0/go.mod h1:z2GW5p5WCZGEqVVIJUdhl232vRbLf2Qu4EDlEakX/D8=
|
apigo.cc/go/cast v1.5.3/go.mod h1:GMjjrYn93tWat1U409G7h1jR3ejfLLI7r0efBo9Sbd4=
|
||||||
apigo.cc/go/config v1.5.1 h1:rpj7oCzlsDV3f2/YK3Pb+CHbfr2DL5Vyyv6VNkobJP4=
|
apigo.cc/go/config v1.5.3 h1:peq1FM2xO+vzPHJf8Dwg3DXm8PtFQMfTFKQj6fpoG7A=
|
||||||
apigo.cc/go/config v1.5.1/go.mod h1:jdMiDLPa9gzB8/FFZvm9jOopUqdxb7XSX+0OeWcZZUM=
|
apigo.cc/go/config v1.5.3/go.mod h1:ZiOAjWa1mQIzszaJZN+kO6YU4GXreng+NxkcK/TAkqQ=
|
||||||
apigo.cc/go/crypto v1.5.0 h1:Nxz7a6VKCdvaF258IU0NkjQyureOLxfR308Sy2iftUI=
|
apigo.cc/go/crypto v1.5.3 h1:2JUHC2cgR2zrnn36EzwkUAdxmmTXAA/8yTNo+2X1mPE=
|
||||||
apigo.cc/go/crypto v1.5.0/go.mod h1:F9M6nXv+5328r1ZwbTvI6fcr8VdgqHVzALOcsdv6ntE=
|
apigo.cc/go/crypto v1.5.3/go.mod h1:PheYKHEXmoEFI1AK5PpY1borQWcRlkkSaWncT3cWbhE=
|
||||||
apigo.cc/go/discover v1.5.0 h1:RGHulidyAHCZdGfpFytFUl3ur4aNVMXKlfJbAMCvgpo=
|
apigo.cc/go/discover v1.5.3 h1:WW1A7qReYytebETDb5MhVRPmT10KReGM4QbPTfqS/iw=
|
||||||
apigo.cc/go/discover v1.5.0/go.mod h1:nA5DQlmhzjGBZVqpEbZRNDIKNU+Sr9trxbKAgDxjZ+I=
|
apigo.cc/go/discover v1.5.3/go.mod h1:rylFiGvfNro090rGtlc9K31KATn6WZbnn1Fk2AbQZEU=
|
||||||
apigo.cc/go/encoding v1.5.0 h1:EJNdRVDOMoI2DAvZwQNQTbYuqB/6zsEzvg7lS5pQI+I=
|
apigo.cc/go/encoding v1.5.4 h1:Fk8TrveZATyy8SHukC4ZiqdTSp+QIfsRHtt55xmMK7w=
|
||||||
apigo.cc/go/encoding v1.5.0/go.mod h1:8++NfZj3hWig0qh2g7GQRw/4LpSvCYMWUZ+8J+x58cA=
|
apigo.cc/go/encoding v1.5.4/go.mod h1:dShEsZ3gKqBINz7TSOYf4e7/fBCqCY9VzlenoGUQUFM=
|
||||||
apigo.cc/go/file v1.5.0 h1:Fh1NSDBqaxjuXYJ71yPHPXVJ8BFEv/AGS3l+jkLi5uw=
|
apigo.cc/go/file v1.5.5 h1:/+HmDumLu6Qk2KuQL63M9lpgzHTDL+QJ8dStOl7e9gs=
|
||||||
apigo.cc/go/file v1.5.0/go.mod h1:4YhOGgBINTpmmmgws3H8LAyXQQBGzBp44hYUoCS+kr0=
|
apigo.cc/go/file v1.5.5/go.mod h1:xRVNhctvqOKeBemmcRW/BQfgkc3B+vT/UZVdSc7duUo=
|
||||||
apigo.cc/go/http v1.5.0 h1:GGIu0dhMjTiYygxH9NWOzz6AY+WZjfyTL1qZ8G9vI1U=
|
apigo.cc/go/http v1.5.3 h1:nvJh9bqPPcPRv6p8WEw7bJAd0UC+r2zvQA8/QioVLTQ=
|
||||||
apigo.cc/go/http v1.5.0/go.mod h1:CIIH7HS6wdicLpSgkEVozdDcHlM9W9ygmmzJvzhAKWg=
|
apigo.cc/go/http v1.5.3/go.mod h1:cFrPK61y9f1PrsNSJscZT/QVOgkT15o9OP7O8cuMb8Q=
|
||||||
apigo.cc/go/id v1.5.0 h1:MjNWPhBhDsoXaLeJDv/0wfJmVMU9EvOs8pWYfsTQ6e8=
|
apigo.cc/go/id v1.5.4 h1:D1Zx9gEZhOgdTgZ4SdmPImhpc9xGiOA33Y+j2MkstzQ=
|
||||||
apigo.cc/go/id v1.5.0/go.mod h1:qhu4a1/KLc/XcBpcsRu+mXZt7U7Wvd9zMcPs4VspuPA=
|
apigo.cc/go/id v1.5.4/go.mod h1:hCTQq+KC1ALWe1FpPERf+W4B6FSulg9FAgOUJDDySiY=
|
||||||
apigo.cc/go/jsmod v1.5.0 h1:JgQtJNiJWy1NOP9AzE8NX5VXJkpO/x3GqLsCCSny5Ec=
|
apigo.cc/go/jsmod v1.5.3 h1:S3W317bH0QV2NMeRO1E0v6ySIBOfMWYv/NuQJbvqKWU=
|
||||||
apigo.cc/go/jsmod v1.5.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=
|
apigo.cc/go/jsmod v1.5.3/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=
|
||||||
apigo.cc/go/log v1.5.5 h1:AFU7d7AQxkpgDHl7SnlEwd6yzGSFAlnrrjbrNDQnQHI=
|
apigo.cc/go/log v1.5.8 h1:/IYtGPWhRjT3OayylDIphkWZIQbpLjqVeSnFEiD3Dy0=
|
||||||
apigo.cc/go/rand v1.5.0 h1:1o8hh8fhdBuk1/h02IvugvamuT3dkWbVJrqEJVQKB2E=
|
apigo.cc/go/log v1.5.8/go.mod h1:HfFPANMYxJx197SSTXB21Pgxcz/gGqPP8nlSErgd5WE=
|
||||||
apigo.cc/go/rand v1.5.0/go.mod h1:Lh98S2dm9UY0X+M+kNQQEKyXHG5pcCKSFPyXN0QCGdk=
|
apigo.cc/go/rand v1.5.3 h1:O4bPIwyaOWEBCr0nL9A4G4qG48AqiGTCzfPeckm3Ius=
|
||||||
apigo.cc/go/redis v1.5.0 h1:VXNDqzKj87BchF7ubDEH+T6lp8NrjeK0izU4ooo7u1A=
|
apigo.cc/go/rand v1.5.3/go.mod h1:q1BTFkY/cXE229dDD5Q22lF7T0DoKPV6xAu+6bCrDH4=
|
||||||
apigo.cc/go/redis v1.5.0/go.mod h1:/olsrHndkUNezUX1KbBBt8b4Got7SX7E8EJzcb1PknM=
|
apigo.cc/go/redis v1.5.6 h1:Lzo8M2binfqdQdVVp31Z/Max4qT8D82QdZjLlLQsrIY=
|
||||||
apigo.cc/go/safe v1.5.0 h1:W1NblmcU8cex1f9Y5z8mNLUJOzZTE1s6fszb3FbhGnk=
|
apigo.cc/go/redis v1.5.6/go.mod h1:HmqSh2Ll7/b2zFXDi2Ap13YOuMCVniuZNbwtxkbIYII=
|
||||||
apigo.cc/go/safe v1.5.0/go.mod h1:OfQ5d6COePSGEuPvMeOk6KagX2sezw7nvKh7exj9SeM=
|
apigo.cc/go/safe v1.5.2 h1:EnuEOW/SGwf/5A0nw9LnqfKJE071+TIc6ez8HI9R9Lg=
|
||||||
apigo.cc/go/shell v1.5.0 h1:WLDMMqUU0INeaBDmQsTPr0h/NfB2RknAtiJ5NL467+Q=
|
apigo.cc/go/safe v1.5.2/go.mod h1:2GqCCLLGex4OAhdET3iBWm1R+LIYtmTrvHP8W0iESSw=
|
||||||
apigo.cc/go/shell v1.5.0/go.mod h1:rYHA77d5hEsQHcJrbAWf1pHy0sxayeJ0gU55LA/JWQk=
|
apigo.cc/go/shell v1.5.3 h1:pI+u12sy6upoygq+1XXqUlvUboBfH4Q52jRpoJFv56A=
|
||||||
apigo.cc/go/starter v1.5.3 h1:kakDapul+l63w3Ah1pnBxD1mup9Fbt821omWCiaGwCE=
|
apigo.cc/go/shell v1.5.3/go.mod h1:FdZWUrcXHGJXo725oSyHqAeFoX0E9yY3PDhrz9hujgY=
|
||||||
|
apigo.cc/go/starter v1.5.5 h1:4ST02o4qP8IIekxtd9Jhx5RHTrSGXtVQUguSIXV0iWc=
|
||||||
|
apigo.cc/go/starter v1.5.5/go.mod h1:WAGhdtmZdpP1Jn/z0pCqHwpTbqqaFhm5OqH7QVtcanY=
|
||||||
apigo.cc/go/timer v1.5.0 h1:iPo/IQn+iuhBRI1/MR1txwZnamef/RBBfOiIlBiqkgk=
|
apigo.cc/go/timer v1.5.0 h1:iPo/IQn+iuhBRI1/MR1txwZnamef/RBBfOiIlBiqkgk=
|
||||||
apigo.cc/go/timer v1.5.0/go.mod h1:kOnqTTX+zA4AH7SfC+LpUm4ZvS+DVyWWMqul/V5QWJs=
|
apigo.cc/go/timer v1.5.0/go.mod h1:kOnqTTX+zA4AH7SfC+LpUm4ZvS+DVyWWMqul/V5QWJs=
|
||||||
|
apigo.cc/go/watch v1.5.2 h1:zG56PD8Vml5gVJeo2yNuX9s6stOaIJRYqqKGqXK+xTU=
|
||||||
|
apigo.cc/go/watch v1.5.2/go.mod h1:MW1XnI0MVyUZpRy590no1vrDT+U4y20L0UZW9/LgU+k=
|
||||||
|
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||||
|
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||||
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
|||||||
@ -22,6 +22,9 @@ type RouteHandler struct {
|
|||||||
|
|
||||||
func (rh *RouteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (rh *RouteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
ws := rh.ws
|
ws := rh.ws
|
||||||
|
if ws == nil {
|
||||||
|
ws = DefaultServer
|
||||||
|
}
|
||||||
atomic.AddInt64(&rh.webRequestingNum, 1)
|
atomic.AddInt64(&rh.webRequestingNum, 1)
|
||||||
defer atomic.AddInt64(&rh.webRequestingNum, -1)
|
defer atomic.AddInt64(&rh.webRequestingNum, -1)
|
||||||
|
|
||||||
|
|||||||
32
js_export.go
32
js_export.go
@ -7,17 +7,31 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
jsmod.Register("service", map[string]any{
|
jsmod.Register("service", map[string]any{
|
||||||
// 类型占位工厂 (用于 AI 发现类型结构,生成文档时隐藏)
|
// 类型占位工厂 (用于 AI 发现类型结构,生成文档时隐藏)
|
||||||
"__exportRequest": func() *Request { return &Request{} },
|
"__exportRequest": jsExportRequest,
|
||||||
"__exportResponse": func() *Response { return &Response{} },
|
"__exportResponse": jsExportResponse,
|
||||||
"__exportWebSocket": func() *WebSocketConn { return &WebSocketConn{} },
|
"__exportWebSocket": jsExportWebSocket,
|
||||||
"__exportSession": func() *Session { return &Session{} },
|
"__exportSession": jsExportSession,
|
||||||
"__exportFile": func() *jsUploadFile { return &jsUploadFile{} },
|
"__exportFile": jsExportFile,
|
||||||
|
|
||||||
// 功能函数
|
// 功能函数
|
||||||
"Upgrade": Upgrade,
|
"Upgrade": jsUpgrade,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func jsExportRequest() *Request { return &Request{} }
|
||||||
|
func jsExportResponse() *Response { return &Response{} }
|
||||||
|
func jsExportWebSocket() *WebSocketConn { return &WebSocketConn{} }
|
||||||
|
func jsExportSession() *Session { return &Session{} }
|
||||||
|
func jsExportFile() *jsUploadFile { return &jsUploadFile{} }
|
||||||
|
|
||||||
|
func jsUpgrade(response *Response, request *Request) (*WebSocketConn, error) {
|
||||||
|
conn, err := Upgrade(response, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
// jsUploadFile 包装 UploadFile 以隐藏敏感方法
|
// jsUploadFile 包装 UploadFile 以隐藏敏感方法
|
||||||
type jsUploadFile struct {
|
type jsUploadFile struct {
|
||||||
f *UploadFile
|
f *UploadFile
|
||||||
@ -41,5 +55,9 @@ func (j *jsUploadFile) Content() ([]byte, error) {
|
|||||||
if j.f == nil {
|
if j.f == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return j.f.Content()
|
res, err := j.f.Content()
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|||||||
19
response.go
19
response.go
@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"apigo.cc/go/cast"
|
"apigo.cc/go/cast"
|
||||||
"apigo.cc/go/file"
|
"apigo.cc/go/file"
|
||||||
|
"apigo.cc/go/jsmod"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -74,7 +75,11 @@ func (r *Response) Write(bytes []byte) (int, error) {
|
|||||||
if r.ProxyHeader != nil {
|
if r.ProxyHeader != nil {
|
||||||
r.copyProxyHeader()
|
r.copyProxyHeader()
|
||||||
}
|
}
|
||||||
return r.Writer.Write(bytes)
|
n, err := r.Writer.Write(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return n, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PhysicalWrite 物理写入网线,绕过过滤器缓冲逻辑
|
// PhysicalWrite 物理写入网线,绕过过滤器缓冲逻辑
|
||||||
@ -83,12 +88,20 @@ func (r *Response) PhysicalWrite(bytes []byte) (int, error) {
|
|||||||
if r.ProxyHeader != nil {
|
if r.ProxyHeader != nil {
|
||||||
r.copyProxyHeader()
|
r.copyProxyHeader()
|
||||||
}
|
}
|
||||||
return r.Writer.Write(bytes)
|
n, err := r.Writer.Write(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return n, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteString 写入字符串响应
|
// WriteString 写入字符串响应
|
||||||
func (r *Response) WriteString(s string) (int, error) {
|
func (r *Response) WriteString(s string) (int, error) {
|
||||||
return r.Write([]byte(s))
|
n, err := r.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
return n, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHeader 设置响应状态码
|
// WriteHeader 设置响应状态码
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"apigo.cc/go/cast"
|
"apigo.cc/go/cast"
|
||||||
|
"apigo.cc/go/jsmod"
|
||||||
"apigo.cc/go/log"
|
"apigo.cc/go/log"
|
||||||
"apigo.cc/go/redis"
|
"apigo.cc/go/redis"
|
||||||
"errors"
|
"errors"
|
||||||
@ -125,7 +126,7 @@ func (s *Session) Save() error {
|
|||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
if !s.conn.SETEX("SESS_"+s.id, timeout, s.data) {
|
if !s.conn.SETEX("SESS_"+s.id, timeout, s.data) {
|
||||||
return errors.New("redis save failed")
|
return jsmod.MakeError(errors.New("redis save failed"))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
34
websocket.go
34
websocket.go
@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"apigo.cc/go/jsmod"
|
||||||
"apigo.cc/go/log"
|
"apigo.cc/go/log"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -18,43 +19,60 @@ type WebSocketConn struct {
|
|||||||
|
|
||||||
// Send 发送消息,支持 string, []byte 或 自动转 JSON
|
// Send 发送消息,支持 string, []byte 或 自动转 JSON
|
||||||
func (c *WebSocketConn) Send(data any) error {
|
func (c *WebSocketConn) Send(data any) error {
|
||||||
|
var err error
|
||||||
switch v := data.(type) {
|
switch v := data.(type) {
|
||||||
case string:
|
case string:
|
||||||
return c.Conn.WriteMessage(websocket.TextMessage, []byte(v))
|
err = c.Conn.WriteMessage(websocket.TextMessage, []byte(v))
|
||||||
case []byte:
|
case []byte:
|
||||||
return c.Conn.WriteMessage(websocket.BinaryMessage, v)
|
err = c.Conn.WriteMessage(websocket.BinaryMessage, v)
|
||||||
default:
|
default:
|
||||||
return c.Conn.WriteJSON(v)
|
err = c.Conn.WriteJSON(v)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadString 读取字符串消息
|
// ReadString 读取字符串消息
|
||||||
func (c *WebSocketConn) ReadString() (string, error) {
|
func (c *WebSocketConn) ReadString() (string, error) {
|
||||||
_, b, err := c.Conn.ReadMessage()
|
_, b, err := c.Conn.ReadMessage()
|
||||||
return string(b), err
|
if err != nil {
|
||||||
|
return "", jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return string(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadBytes 读取二进制消息
|
// ReadBytes 读取二进制消息
|
||||||
func (c *WebSocketConn) ReadBytes() ([]byte, error) {
|
func (c *WebSocketConn) ReadBytes() ([]byte, error) {
|
||||||
_, b, err := c.Conn.ReadMessage()
|
_, b, err := c.Conn.ReadMessage()
|
||||||
return b, err
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadJSON 读取 JSON 消息
|
// ReadJSON 读取 JSON 消息
|
||||||
func (c *WebSocketConn) ReadJSON(v any) error {
|
func (c *WebSocketConn) ReadJSON(v any) error {
|
||||||
return c.Conn.ReadJSON(v)
|
if err := c.Conn.ReadJSON(v); err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close 关闭连接
|
// Close 关闭连接
|
||||||
func (c *WebSocketConn) Close() error {
|
func (c *WebSocketConn) Close() error {
|
||||||
return c.Conn.Close()
|
if err := c.Conn.Close(); err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade 将 HTTP 请求升级为 WebSocket 连接
|
// Upgrade 将 HTTP 请求升级为 WebSocket 连接
|
||||||
func Upgrade(response *Response, request *Request) (*WebSocketConn, error) {
|
func Upgrade(response *Response, request *Request) (*WebSocketConn, error) {
|
||||||
conn, err := defaultUpgrader.Upgrade(response.Writer, request.Request, nil)
|
conn, err := defaultUpgrader.Upgrade(response.Writer, request.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, jsmod.MakeError(err)
|
||||||
}
|
}
|
||||||
return &WebSocketConn{Conn: conn}, nil
|
return &WebSocketConn{Conn: conn}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user