107 Commits

Author SHA1 Message Date
ab78127317 引入 GinWrapperContext, gateway待升级测试 2025-03-10 14:44:20 +08:00
28da213bb8 优化 GinContext 定义 2025-03-10 14:07:51 +08:00
13d1ab6607 merge master && fix both modify 2025-03-10 12:29:11 +08:00
2fd9195b77 Merge pull request '修复logic after response实际是同步逻辑问题' (#9) from feature/fix_logic_after_response into master
Reviewed-on: #9
2025-02-28 18:43:11 +08:00
c826166256 修复logic after response实际是同步逻辑问题 2025-02-28 18:18:03 +08:00
84f0e1a4f6 Merge pull request '引入对响应后处理逻辑的支持' (#8) from feature/logic_after_response into master
Reviewed-on: #8
2025-02-28 17:05:12 +08:00
9ae310782b fix 2025-02-28 17:04:36 +08:00
0d981b32c6 增加获取logic after response实例的方法 2025-02-28 16:57:02 +08:00
9decd12ee8 优化代码组织 2025-02-28 16:53:02 +08:00
b95d464c89 包装handler支持响应后触发相关逻辑 2025-02-28 16:48:22 +08:00
bb5d97f6eb 规划二次包装的ginContext 2025-02-28 12:26:09 +08:00
ac5776f3f6 增加ctx copy的包装方法 2025-02-28 11:44:56 +08:00
3432087fbd 修复拼写错误 2025-02-25 17:22:14 +08:00
2c201ae226 优化跨域处理 2025-02-22 17:05:38 +08:00
12d2735663 Merge pull request '升级路由注册' (#7) from feature/upgrade_route_register into master
Reviewed-on: #7
2025-02-22 16:15:37 +08:00
01b62b6eee 支持在router实力上自行注册路由 2025-02-22 16:14:55 +08:00
1de0715637 支持注册404Handler 2025-02-22 16:05:56 +08:00
93cef0cf7f 支持请求日志记录中间件 2025-02-22 15:45:30 +08:00
b9a7f97342 优化注册全局中间件逻辑 2025-02-22 15:00:37 +08:00
75cc21c494 Merge pull request '支持注册pprof相关路由' (#6) from feature/support_pprof into master
Reviewed-on: #6
2025-02-21 15:17:09 +08:00
4909e75928 支持注册pprof相关路由 2025-02-21 15:16:43 +08:00
bc94242d12 update apiDocPkg 2025-02-20 18:52:49 +08:00
11d90566ae update apiDocPkg 2025-02-20 18:01:26 +08:00
113b1e5a05 升级apiDocPkg 2025-02-20 16:24:08 +08:00
7530625baa update go sum 2025-02-20 10:38:00 +08:00
8cb98e4817 update apiDocPkg 2025-02-19 22:26:45 +08:00
cfcb05f4da update go mod 2025-02-19 21:49:49 +08:00
f11540fa6e update apiDocPkg 2025-02-19 18:17:15 +08:00
c3e42b944c update go mod 2025-02-19 15:37:59 +08:00
bb54978dc5 update go mod 2025-02-18 23:04:44 +08:00
de34f57a5f update go mod 2025-02-18 22:31:06 +08:00
793fefbcd2 update go mod 2025-02-18 21:29:01 +08:00
26476ee23b Merge pull request 'server初始化支持设置各种option' (#5) from feature/support_server_option into master
Reviewed-on: #5
2025-02-18 18:14:04 +08:00
1c2c5aa4ce server初始化支持设置各种option 2025-02-18 18:12:44 +08:00
74dde8b41e update go mod 2025-02-18 17:27:03 +08:00
1e53d899dc update go mod 2025-02-18 14:57:16 +08:00
a80c559a7c 修复文档路由重复注册的BUG 2025-02-18 14:39:29 +08:00
02675d6c40 update go mod 2025-02-18 14:09:24 +08:00
681bd3fe95 update api-doc 2025-02-17 18:12:52 +08:00
7ef8bf2676 修复参数解析BUG 2025-02-17 17:13:57 +08:00
293f829945 反射支持 controller 指针与普通实例; 函数调用支持指针参数与非指针参数 2025-02-17 16:31:21 +08:00
74f0ae2069 update go mod 2025-02-16 21:12:28 +08:00
d237b3b651 update go mod 2025-02-16 20:08:17 +08:00
fca399fdc3 Merge pull request '升级server自动管理' (#4) from feature/upgrade_server into master
Reviewed-on: #4
2025-02-16 17:45:07 +08:00
593df47884 引入日志初始化中间件 2025-02-16 17:41:33 +08:00
3774bda0ff 升级api-doc版本 2025-02-16 17:28:22 +08:00
4ddf55d371 优化接口文档注册 2025-02-16 16:44:57 +08:00
d832eef5a0 调试文档集成 2025-02-16 13:06:58 +08:00
c3df76e94d 修复URL注册BUG 2025-02-15 22:34:21 +08:00
99df73e50e 整合默认swagger文档 2025-02-15 21:53:21 +08:00
ff91efd1c8 完成一版文档json生成 2025-02-15 21:19:35 +08:00
e1f597ae50 引入文档解析 2025-02-14 22:30:56 +08:00
2f7d1438b2 update go mod 2025-02-14 21:16:19 +08:00
a50883b734 优化server初始化的方式 2025-02-07 18:33:07 +08:00
5d19efa8cf Merge pull request '格式化验证失败的错误信息, 默认tag=err, 支持重置默认tag' (#3) from feature/format_validate_err into master
Reviewed-on: #3
2025-02-07 18:03:21 +08:00
7c67160c65 格式化验证失败的错误信息, 默认tag=err, 支持重置默认tag 2025-02-07 18:01:05 +08:00
a338713c77 Merge pull request '支持通过controller自动解析注册接口路由' (#2) from feature/router into master
Reviewed-on: #2
2025-02-07 17:32:34 +08:00
cb4718a269 接口逻辑函数返回值严格校验时, 校验不通过, 由忽略接口注册修改为panic 2025-02-07 17:25:25 +08:00
1b1964881f 返回数据支持是否严格模式的校验, 严格模式下, 必须返回结构体或者map 2025-02-07 17:19:25 +08:00
e95061a1a8 完成基础反射调用接口 + 数据响应 2025-02-07 16:57:15 +08:00
77ea723e86 完善 RequestHandler 逻辑 2025-02-07 16:10:33 +08:00
196c437cc5 update go mod 2025-02-07 10:33:39 +08:00
b408076fa7 保存路由统一注册方法 2025-01-27 19:46:34 +08:00
851de1b3ef save code 2025-01-27 15:07:58 +08:00
e40475cdb1 update go mod 2025-01-26 14:34:24 +08:00
ec04a023cd merge master && fix both modify 2025-01-26 14:32:59 +08:00
18988c4d46 Merge pull request '升级请求体解析' (#1) from feature/parse_body into master
Reviewed-on: #1
2024-11-05 17:32:44 +08:00
196c420fcc update go mod 2024-11-05 17:31:44 +08:00
6a917d338a 支持xml body解析 2024-11-04 17:06:51 +08:00
f6db9e8edb 升级 wrapperHandle -> ParseBody 2024-11-04 16:12:09 +08:00
a0b4cdb414 update go mod 2024-11-04 15:57:39 +08:00
cffb0c8779 增加form url encode解析 2024-10-22 17:20:30 +08:00
56441151cf 增加json的请求Body解析 2024-10-22 17:14:53 +08:00
2c99eb9656 涉及请求体解析适配器接口约束 2024-10-22 16:49:45 +08:00
a72733d81a update go mod 2024-10-22 16:37:13 +08:00
0636dd1b11 优化异常堆栈打印 2024-09-30 16:17:14 +08:00
e52d67cfe3 update 2024-09-30 15:54:20 +08:00
02cdc3c792 fix 2024-09-30 15:25:58 +08:00
36d4ca844a update debug strack 2024-09-30 15:25:17 +08:00
99ea9ba111 接口非成功打印异常堆栈
- 接口非成功打印异常堆栈
- 支持开关控制是否打印
2024-09-30 11:47:04 +08:00
8ceb818a24 fix 2024-09-26 17:40:09 +08:00
22bf77019a update method name 2024-09-26 17:33:27 +08:00
01c2fdf5a2 修复对外输出扩展数据 2024-09-24 15:10:37 +08:00
29b0eaa6b3 fix middleware 2024-09-24 15:05:02 +08:00
5ae2e3fae1 数据相应支持扩展数据 2024-09-24 14:56:03 +08:00
9d0f74b19a 增加解析header + 解析 cookie 2024-09-23 17:06:49 +08:00
c4d41a9d4e fix return value 2024-09-20 17:37:03 +08:00
5abd91f947 set defult支持get 2024-09-20 17:34:53 +08:00
4f1e0e2649 表单验证支持设置默认值 2024-09-20 17:31:23 +08:00
df5a20ea87 增加GetDomain方法 2024-09-19 15:13:25 +08:00
195e391235 修复响应完成时间未设置的问题 2024-08-17 20:21:34 +08:00
ce8feaa98b 修复响应完成时间未设置的问题 2024-08-17 20:07:48 +08:00
29bcf44ec9 相应日志区区成功和失败 2024-08-17 17:31:11 +08:00
c420abfed9 update go mod 2024-08-14 14:12:37 +08:00
fce6043dca update go mod 2024-08-11 17:39:03 +08:00
df16e63e85 fix both modify 2024-07-26 14:47:00 +08:00
9b7e714b29 fix 2024-07-26 14:10:47 +08:00
b9c0389e7b 升级请求日志记录 + 升级 request wrapperHandle + 拆分after hook 2024-07-26 12:25:22 +08:00
b2fe05111c fix 2024-07-26 11:52:53 +08:00
3c29aa847c 增加获取query和body的方法 2024-07-26 11:50:42 +08:00
3368418b3b update go mod 2024-07-26 11:07:14 +08:00
86cecc4eb7 init 2024-07-23 21:53:00 +08:00
a1418c290c fix 2024-07-23 21:40:53 +08:00
8f533f5b04 fix 2024-07-23 21:34:40 +08:00
20d655973a 修复initRequest middleware 2024-07-23 18:29:31 +08:00
ca0cb31235 优化响应配置 2024-07-23 17:09:29 +08:00
9eb3b92c41 增加wrapper方法 2024-07-23 16:40:13 +08:00
31 changed files with 2062 additions and 290 deletions

12
define/request.go Normal file
View File

@ -0,0 +1,12 @@
// Package define ...
//
// Description : define ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-03-10 14:38
package define
const (
GinWrapperContextKey = "GIN_WRAPPER_CONTEXT"
)

View File

@ -7,31 +7,14 @@
// Date : 2022-06-25 20:33
package define
import (
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/wrapper"
"sync"
)
var (
// RequestIDField 请求ID
RequestIDField = "request_id"
// TraceIDField 追踪ID
TraceIDField = "trace_id"
// StartRequestTimeField 开始请求时间字段
StartRequestTimeField = "start_request_time"
// FinishRequestTimeField 完成请求时间
FinishRequestTimeField = "finish_request_time"
// ResponseCodeField 响应状态码字段
ResponseCodeField = "code"
// ResponseMessageField 响应信息字段
ResponseMessageField = "message"
// HandleRequestCostField 处理请求耗时
HandleRequestCostField = "cost"
// ResponseDataField 响应数据字段
ResponseDataField = "data"
// ResponseTraceIDField 响应TraceID字段
ResponseTraceIDField = "trace_id"
// ResponseRequestIDField 响应的请求ID字段
ResponseRequestIDField = "request_id"
// RecordResponseDataField 记录响应数据到上下文
RecordResponseDataField = "pkg_gin_record_response_data"
// RecordRequestDataField 记录请求数据到上下文
RecordRequestDataField = "pkg_gin_record_request_data"
inputHttpHandleConfig = &HttpHandleConfig{}
)
// HttpHandleConfig 请求处理配置
@ -40,15 +23,21 @@ var (
//
// Date : 20:41 2022/6/25
type HttpHandleConfig struct {
RequestIDField string
TraceIDField string
ResponseCodeField string
ResponseMessageField string
HandleRequestCostField string
ResponseDataField string
ResponseTraceIDField string
ResponseRequestIDField string
StartRequestTimeField string
RecordRequestDataField string
RecordResponseDataField string
RequestIDField string
TraceIDField string
ResponseCodeField string
ResponseMessageField string
HandleRequestCostField string
ResponseDataField string
ResponseTraceIDField string
StartRequestTimeField string
FinishRequestTimeField string
RequestIsSuccessField string // 请求处理是否成功的标识
ExtensionOutputField string // 扩展信息对外输出字段
EnableExtensionOutput bool
DisableDebugStackOutput bool // 禁用异常堆栈打印
}
// ConvertDefaultConfig 覆盖默认配置
@ -57,39 +46,103 @@ type HttpHandleConfig struct {
//
// Date : 20:41 2022/6/25
func ConvertDefaultConfig(cfg *HttpHandleConfig) {
if len(cfg.RequestIDField) > 0 {
RequestIDField = cfg.ResponseRequestIDField
}
inputHttpHandleConfig = cfg
}
if len(cfg.TraceIDField) > 0 {
TraceIDField = cfg.TraceIDField
}
if len(cfg.ResponseCodeField) > 0 {
ResponseCodeField = cfg.ResponseCodeField
}
if len(cfg.ResponseMessageField) > 0 {
ResponseMessageField = cfg.ResponseMessageField
}
if len(cfg.ResponseDataField) > 0 {
ResponseDataField = cfg.ResponseDataField
}
if len(cfg.ResponseRequestIDField) > 0 {
ResponseRequestIDField = cfg.ResponseRequestIDField
}
if len(cfg.ResponseTraceIDField) > 0 {
ResponseTraceIDField = cfg.ResponseTraceIDField
}
if len(cfg.HandleRequestCostField) > 0 {
HandleRequestCostField = cfg.HandleRequestCostField
}
if len(cfg.StartRequestTimeField) > 0 {
StartRequestTimeField = cfg.StartRequestTimeField
// GetHttpHandleConfig 获取http配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:55 2024/7/23
func GetHttpHandleConfig() *HttpHandleConfig {
return &HttpHandleConfig{
EnableExtensionOutput: inputHttpHandleConfig.EnableExtensionOutput,
DisableDebugStackOutput: inputHttpHandleConfig.DisableDebugStackOutput,
RequestIDField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.RequestIDField == "",
consts.GinRequestIDField,
wrapper.String(inputHttpHandleConfig.RequestIDField),
).Value(),
TraceIDField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.TraceIDField == "",
consts.GinTraceIDField,
wrapper.String(inputHttpHandleConfig.TraceIDField),
).Value(),
ResponseCodeField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseCodeField == "",
consts.GinResponseCodeField,
wrapper.String(inputHttpHandleConfig.ResponseCodeField),
).Value(),
ResponseMessageField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseMessageField == "",
consts.GinResponseMessageField,
wrapper.String(inputHttpHandleConfig.ResponseMessageField),
).Value(),
HandleRequestCostField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.HandleRequestCostField == "",
consts.GinHandleRequestCostField,
wrapper.String(inputHttpHandleConfig.HandleRequestCostField),
).Value(),
ResponseDataField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseDataField == "",
consts.GinResponseDataField,
wrapper.String(inputHttpHandleConfig.ResponseDataField),
).Value(),
ResponseTraceIDField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseTraceIDField == "",
consts.GinResponseTraceIDField,
wrapper.String(inputHttpHandleConfig.ResponseTraceIDField),
).Value(),
StartRequestTimeField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.StartRequestTimeField == "",
consts.GinStartRequestTimeField,
wrapper.String(inputHttpHandleConfig.StartRequestTimeField),
).Value(),
FinishRequestTimeField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.FinishRequestTimeField == "",
consts.GinFinishRequestTimeField,
wrapper.String(inputHttpHandleConfig.FinishRequestTimeField),
).Value(),
RecordRequestDataField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.RecordRequestDataField == "",
consts.GinRecordRequestDataField,
wrapper.String(inputHttpHandleConfig.RecordRequestDataField),
).Value(),
RecordResponseDataField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.RecordResponseDataField == "",
consts.GinRecordResponseDataField,
wrapper.String(inputHttpHandleConfig.RecordResponseDataField),
).Value(),
RequestIsSuccessField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.RequestIsSuccessField == "",
consts.GinRequestIsSuccessField,
wrapper.String(inputHttpHandleConfig.RecordResponseDataField),
).Value(),
ExtensionOutputField: wrapper.TernaryOperator.String(
nil == inputHttpHandleConfig || inputHttpHandleConfig.ExtensionOutputField == "",
consts.GinResponseExtensionField,
wrapper.String(inputHttpHandleConfig.RecordResponseDataField),
).Value(),
}
}
const (
LogicAfterResponseKey = "__logic_after_response__"
)
type LogicAfterResponse struct {
SuccessHookFuncList []func() `json:"-"` // 请求最后需要执行的成功hook函数
FailureHookFuncList []func() `json:"-"` // 请求最后需要执行的失败hook函数
Lock *sync.RWMutex `json:"-"` // 逻辑锁
}
func (logic *LogicAfterResponse) AddSuccessHook(f func()) {
logic.Lock.Lock()
defer logic.Lock.Unlock()
logic.SuccessHookFuncList = append(logic.SuccessHookFuncList, f)
}
func (logic *LogicAfterResponse) AddFailureHook(f func()) {
logic.Lock.Lock()
defer logic.Lock.Unlock()
logic.FailureHookFuncList = append(logic.FailureHookFuncList, f)
}

72
go.mod
View File

@ -1,59 +1,83 @@
module git.zhangdeman.cn/zhangdeman/gin
go 1.21
go 1.23.0
toolchain go1.21.5
toolchain go1.24.0
require (
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20231105153815-e8561a060cc8
git.zhangdeman.cn/gateway/api-doc v0.0.0-20250220105101-71d6db967dc5
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091724-ca151fbc1f06
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20241125083316-eab7bab9d7ad
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240627031706-9ff1c213bb50
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20231220041950-807f3d74a6fa
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9
github.com/gin-contrib/pprof v1.5.2
github.com/gin-gonic/gin v1.10.0
github.com/go-playground/validator/v10 v10.22.0
github.com/go-playground/validator/v10 v10.25.0
github.com/mcuadros/go-defaults v1.2.0
github.com/sbabiv/xml2map v1.2.1
go.uber.org/zap v1.27.0
)
require (
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240719075638-afb5d1d933ce // indirect
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 // indirect
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda // indirect
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 // indirect
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 // indirect
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd // indirect
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
github.com/bytedance/sonic v1.11.9 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/bytedance/sonic v1.12.9 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/go-webtools/knife4go v1.0.4 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
github.com/lestrrat-go/strftime v1.1.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mozillazg/go-pinyin v0.20.0 // indirect
github.com/mssola/user_agent v0.6.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tidwall/gjson v1.17.1 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/swaggo/gin-swagger v1.6.0 // indirect
github.com/swaggo/swag v1.16.4 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
golang.org/x/arch v0.14.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.30.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

197
go.sum
View File

@ -1,69 +1,121 @@
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240719075638-afb5d1d933ce h1:DH01rT/F/UNMqXm5HIv+Q2oKPSkg8rjy1TK8pE83rpU=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240719075638-afb5d1d933ce/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0=
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20231105153815-e8561a060cc8 h1:q9pXs8ByVg/XwyDopIGyfEOi/LyHFR0r1rCnJ90uFxw=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20231105153815-e8561a060cc8/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
git.zhangdeman.cn/gateway/api-doc v0.0.0-20250220105101-71d6db967dc5 h1:UV2+nD9HQLbRt+vY98gwPtkd+M8fbfffl7nb4Mkymmc=
git.zhangdeman.cn/gateway/api-doc v0.0.0-20250220105101-71d6db967dc5/go.mod h1:oNTwnepzW09RNfIQCpgmbiayTKLvGx2XP92esfkxv98=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8 h1:VEifPc+vkpEQoX9rj7zxmT1m+IA81XjOxe7+Z1aqWNM=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0=
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 h1:s6d4b6yY+NaK1AzoBD1pxqsuygEHQz0Oie86c45geDw=
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4/go.mod h1:V4Dfg1v/JVIZGEKCm6/aehs8hK+Xow1dkL1yiQymXlQ=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091724-ca151fbc1f06 h1:XsjGMkBCi93h56oCg5Lrz5zVpUxify/CQVhQU9+qLWM=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091724-ca151fbc1f06/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20241125083316-eab7bab9d7ad h1:6BI3QiDI64SlER1006UJbTJyOCXxB8KCmCK+Kr7FzQo=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20241125083316-eab7bab9d7ad/go.mod h1:+jPQTyCEQqMWhq4p1LowQWq15emisON+++87ArTgwNA=
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442 h1:1eBf0C0gdpBQOqjTK3UCw/mwzQ/SCodx3iTQtidx9eE=
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442/go.mod h1:hFYWiS+ExIuJJJdwHWy4P3pVHbd/0mpv53qlbhDNdTI=
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y=
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd h1:2Y37waOVCmVvx0Rp8VGEptE2/2JVMImtxB4dKKDk/3w=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd/go.mod h1:6+7whkCmb4sJDIfH3HxNuXRveaM0gCCNWd2uXZqNtIE=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd h1:q7GG14qgXKB4MEXQFOe7/UYebsqMfPaSX80TcPdOosI=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd/go.mod h1:+D6uPSljwHywjVY5WSBY4TRVMj26TN5f5cFGEYMldjs=
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20231220041950-807f3d74a6fa h1:2bZ9VmQF0pIZ+scnN3UuGoXjjKhccnwfIL779QGZArY=
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20231220041950-807f3d74a6fa/go.mod h1:Bta0kzamTWqBIcc6robGAl/iRuyCFbzy45VGbG8L+7Y=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240627031706-9ff1c213bb50 h1:olo34i2Gq5gX7bYPv5TR4X5l5CrYFtu9UCElkYlmL2c=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240627031706-9ff1c213bb50/go.mod h1:US/pcq2vstE3iyxIHf53w8IeXKkZys7bj/ozLWkRYeE=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e h1:YE2Gi+M03UDImIpWa3I7jzSesyfu2RL8x/4ONs5v0oE=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e/go.mod h1:L/7JugxKZL3JP9JP/XDvPAPz0FQXG1u181Su1+u/d1c=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9 h1:yF770WIDNwyiKL0nwmBGmjZvNCLXtHQL4xJyffPjTMU=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9/go.mod h1:I76wxEsWq7KnMQ84elpwTjEqq4I49QFw60tp5h7iGBs=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg=
github.com/bytedance/sonic v1.11.9/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ=
github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
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/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
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/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/pprof v1.5.2 h1:Kcq5W2bA2PBcVtF0MqkQjpvCpwJr+pd7zxcQh2csg7E=
github.com/gin-contrib/pprof v1.5.2/go.mod h1:a1W4CDXwAPm2zql2AKdnT7OVCJdV/oFPhJXVOrDs5Ns=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
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.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
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.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=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-webtools/knife4go v1.0.4 h1:p32SApmM0sx2/Y5p0QfeaGv5KD96R1mj2CaHdyH8jy8=
github.com/go-webtools/knife4go v1.0.4/go.mod h1:trOlXN1tqBJ7R44sHON3exGvzCwjbsVriIHEenry3d8=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg=
github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
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.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/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/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
github.com/lestrrat-go/strftime v1.1.0 h1:gMESpZy44/4pXLO/m+sL0yBd1W6LjgjrrD4a68Gapyg=
github.com/lestrrat-go/strftime v1.1.0/go.mod h1:uzeIB52CeUJenCo1syghlugshMysrqUT51HlxphXVeI=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc=
github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -73,10 +125,17 @@ github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+
github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
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=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/sbabiv/xml2map v1.2.1 h1:1lT7t0hhUvXZCkdxqtq4n8/ZCnwLWGq4rDuDv5XOoFE=
github.com/sbabiv/xml2map v1.2.1/go.mod h1:2TPoAfcaM7+Sd4iriPvzyntb2mx7GY+kkQpB/GQa/eo=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -89,10 +148,16 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@ -102,33 +167,67 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJcorb+a0hJIQ6uakU5gybjfLWHlXo=
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

12
init.go Normal file
View File

@ -0,0 +1,12 @@
// Package gin ...
//
// Description : gin ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 17:01
package gin
import (
_ "git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
)

View File

@ -8,13 +8,16 @@
package middleware
import (
"git.zhangdeman.cn/zhangdeman/wrapper"
"strings"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/gin/request"
"go.uber.org/zap"
"git.zhangdeman.cn/zhangdeman/gin/define"
"git.zhangdeman.cn/zhangdeman/logger"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// fillCfg 填充默认配置
@ -29,19 +32,12 @@ func fillCfg(cfg *AccessConfig) {
if nil == cfg.IsRecordLog {
cfg.IsRecordLog = defaultIsRecordLog
}
if nil == cfg.ExtraFieldList {
cfg.ExtraFieldList = make([]string, 0)
}
if nil == cfg.RequestHeaderList {
cfg.RequestHeaderList = make([]string, 0)
}
if nil == cfg.ResponseHeaderList {
cfg.ResponseHeaderList = make([]string, 0)
}
if nil == cfg.ExtraFieldList {
cfg.ExtraFieldList = make([]string, 0)
}
cfg.ExtraFieldList = append(cfg.ExtraFieldList, define.RecordRequestDataField, define.RecordResponseDataField)
}
// getLogRequestHeader 获取记录的请求header
@ -54,7 +50,7 @@ func getLogRequestHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]strin
headerTable := make(map[string][]string)
if len(cfg.RequestHeaderList) == 0 {
// 全部记录
for headerKey, _ := range ctx.Request.Header {
for headerKey := range ctx.Request.Header {
cfg.RequestHeaderList = append(cfg.RequestHeaderList, headerKey)
}
}
@ -74,7 +70,7 @@ func getLogResponseHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]stri
responseHeaderTable := make(map[string][]string)
if len(cfg.ResponseHeaderList) == 0 {
// 全部记录
for headerKey, _ := range ctx.Writer.Header() {
for headerKey := range ctx.Writer.Header() {
cfg.ResponseHeaderList = append(cfg.ResponseHeaderList, headerKey)
}
zap.Any("pkg_gin_response_header", ctx.Writer.Header())
@ -92,45 +88,38 @@ func getLogResponseHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]stri
// Date : 10:55 2022/7/14
func LogRequest(cfg *AccessConfig) gin.HandlerFunc {
fillCfg(cfg)
handleConfig := define.GetHttpHandleConfig()
return func(ctx *gin.Context) {
// 未传入配置或者未传入日志实例
if nil == cfg || nil == cfg.Logger || !cfg.IsRecordLog(ctx) {
if nil == cfg || nil == cfg.Logger || (nil != cfg.IsRecordLog && !cfg.IsRecordLog(ctx)) {
ctx.Next()
return
}
startRequestTime := request.WrapperHandle.GetCtxIntData(ctx, handleConfig.StartRequestTimeField, 0)
// 记录请求日志
logData := logger.NewLogData(ctx, consts.LogTypeRequest, "", map[string]any{
handleConfig.StartRequestTimeField: startRequestTime, // 开始请求时间
"request_header": getLogRequestHeader(ctx, cfg), // 请求header
"request_query": request.WrapperHandle.ParseQuery(ctx), // 获取请求query
"request_body": request.WrapperHandle.ParseBody(ctx), // 请求body
})
cfg.Logger.Info("接口请求日志记录", logger.ZapLogDataList(logData)...)
ctx.Next()
startRequestTime := wrapper.OwnTime(ctx.GetTime(define.StartRequestTimeField))
logDataList := []zap.Field{
// 开始请求时间
zap.Any(define.StartRequestTimeField, startRequestTime.FormatUnixMilli()),
}
// 结束时间
finishRequestTime := wrapper.OwnTime(ctx.GetTime(define.FinishRequestTimeField))
logDataList = append(
logDataList,
zap.Any(define.FinishRequestTimeField, finishRequestTime.FormatUnixMilli()), // 请求完成时间
zap.Int64("pkg_gin_request_cost", finishRequestTime.UnixMilli()-startRequestTime.UnixMilli()), // 接口耗时
zap.Any("pkg_gin_request_header", getLogRequestHeader(ctx, cfg)), // 请求header
zap.Any("pkg_gin_response_header", getLogResponseHeader(ctx, cfg)), // 响应header
)
// 扩展数据
for _, field := range cfg.ExtraFieldList {
val, _ := ctx.Get(field)
logDataList = append(logDataList, zap.Any(field, val))
finishRequestTime := request.WrapperHandle.GetCtxIntData(ctx, handleConfig.FinishRequestTimeField, 0)
ctx.Set(handleConfig.FinishRequestTimeField, finishRequestTime)
// 记录相应日志
logResponseData := logger.NewLogData(ctx, consts.LogTypeOutput, "", map[string]any{
handleConfig.FinishRequestTimeField: finishRequestTime, // 完成请求时间
"request_cost": finishRequestTime - startRequestTime, // 请求耗时
"response_body": request.WrapperHandle.GetResponseBody(ctx, handleConfig.ResponseDataField, map[string]any{}), // 响应body
"response_header": getLogResponseHeader(ctx, cfg), // 响应header
})
if ctx.GetBool(define.GetHttpHandleConfig().RequestIsSuccessField) {
cfg.Logger.Info("接口响应日志记录", logger.ZapLogDataList(logResponseData)...)
} else {
cfg.Logger.Error("接口响应日志记录", logger.ZapLogDataList(logResponseData)...)
}
cfg.Logger.Info("请求日志记录", logDataList...)
if nil == cfg.FinishHook {
return
}
// hook 不为nil, 自动触发
cfg.FinishHook(
ctx,
[]byte(ctx.GetString(define.RecordRequestDataField)),
ctx.GetStringMap(define.RecordResponseDataField),
finishRequestTime.UnixMilli()-startRequestTime.UnixMilli(),
)
}
}
@ -140,12 +129,10 @@ func LogRequest(cfg *AccessConfig) gin.HandlerFunc {
//
// Date : 11:26 2022/7/14
type AccessConfig struct {
Logger *zap.Logger // 日志实例
RequestHeaderList []string // 要记录哪些header , 不传全部记录
ResponseHeaderList []string // 要记录哪些响应header, 不传全部记录
IsRecordLog func(ctx *gin.Context) bool // 验证当前请求是否记录日志
ExtraFieldList []string // 记录的扩展字段列表,请将相关数据使用 ctx.Set 写入上下文中, 日志会自动记录
FinishHook func(ctx *gin.Context, requestData []byte, responseData map[string]interface{}, cost int64) // 请求处理完成之后, 触发的hook函数
Logger *zap.Logger // 日志实例
RequestHeaderList []string // 要记录哪些header , 不传全部记录
ResponseHeaderList []string // 要记录哪些响应header, 不传全部记录
IsRecordLog func(ctx *gin.Context) bool // 验证当前请求是否记录日志
}
// defaultIsRecordLog 默认仅记录 json api 日志

View File

@ -0,0 +1,38 @@
// Package middleware ...
//
// Description : middleware ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-07-26 11:54
package middleware
import (
"git.zhangdeman.cn/zhangdeman/gin/define"
"git.zhangdeman.cn/zhangdeman/gin/request"
"github.com/gin-gonic/gin"
)
type HookFunc func(ctx *gin.Context, requestData []byte, responseData map[string]interface{}, cost int64)
// HookAfterResponseMiddleware 请求最终处理完成之后执行的中间件
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:55 2024/7/26
func HookAfterResponseMiddleware(hookFunc HookFunc) gin.HandlerFunc {
handleConfig := define.GetHttpHandleConfig()
return func(ctx *gin.Context) {
ctx.Next()
if nil == hookFunc {
return
}
// hook 不为nil, 自动触发
hookFunc(
ctx,
[]byte(ctx.GetString(handleConfig.RecordRequestDataField)),
ctx.GetStringMap(handleConfig.RecordResponseDataField),
request.WrapperHandle.GetCtxIntData(ctx, handleConfig.StartRequestTimeField, 0)-request.WrapperHandle.GetCtxIntData(ctx, handleConfig.StartRequestTimeField, 0),
)
}
}

View File

@ -27,11 +27,12 @@ import (
// Date : 23:08 2022/6/25
func InitRequest() gin.HandlerFunc {
return func(ctx *gin.Context) {
httpHandleConfig := define.GetHttpHandleConfig()
traceID := getTraceID(ctx)
requestID := getRequestID(ctx, traceID)
ctx.Set(define.TraceIDField, traceID)
ctx.Set(define.RequestIDField, requestID)
ctx.Set(define.StartRequestTimeField, time.Now())
ctx.Set(httpHandleConfig.TraceIDField, traceID)
ctx.Set(httpHandleConfig.RequestIDField, requestID)
ctx.Set(httpHandleConfig.StartRequestTimeField, time.Now().UnixMilli())
ctx.Next()
}
}
@ -57,7 +58,7 @@ func getTraceID(ctx *gin.Context) string {
//
// Date : 23:12 2022/6/25
func getRequestID(ctx *gin.Context, traceID string) string {
requestID := ctx.GetHeader("X-Forward-Request-ID")
requestID := ctx.GetHeader("X-Forward-Request-Id")
if len(requestID) > 0 {
return requestID
}

View File

@ -26,9 +26,12 @@ func ValidateBlackIPMiddleware(code interface{}, httpCode int, validateFunc defi
ctx.Next()
return
}
if validateFunc(ctx, networkUtil.IP.GetRemoteIP(ctx.Request)) {
remoteIp := networkUtil.IP.GetRemoteIP(ctx.Request)
if validateFunc(ctx, remoteIp) {
// 命中黑名单
response.Send(ctx, code, httpCode, nil)
response.Send(ctx, code, httpCode, map[string]any{
"remote_ip": remoteIp,
}, nil)
ctx.Abort()
return
}
@ -48,9 +51,12 @@ func ValidateWhiteIPMiddleware(code interface{}, httpCode int, validateFunc defi
ctx.Next()
return
}
if !validateFunc(ctx, networkUtil.IP.GetRemoteIP(ctx.Request)) {
remoteIp := networkUtil.IP.GetRemoteIP(ctx.Request)
if !validateFunc(ctx, remoteIp) {
// 非名单
response.Send(ctx, code, httpCode, nil)
response.Send(ctx, code, httpCode, map[string]any{
"remote_ip": remoteIp,
}, nil)
ctx.Abort()
return
}

View File

@ -0,0 +1,26 @@
// Package abstract ...
//
// Description : abstract ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 16:38
package abstract
import (
"github.com/gin-gonic/gin"
)
// RequestBodyParseAdaptor 解析请求body的接口适配器约束
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:39 2024/10/22
type RequestBodyParseAdaptor interface {
// Parse 解析Body数据解析结果会反序列化至 receiver , 同时, 会以 map 结构返回
Parse(ctx *gin.Context, receiver any) ([]byte, error)
// Unmarshal 自定义反序列化的方法, 为 nil 则使用内置的序列化方式
Unmarshal() func(sourceData []byte, receiver any) error
// ContentType 当前适配器用与解析何种type
ContentType() string
}

200
request/context.go Normal file
View File

@ -0,0 +1,200 @@
// Package request ...
//
// Description : request ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-28 11:45
package request
import (
"fmt"
networkUtil "git.zhangdeman.cn/zhangdeman/network/util"
"git.zhangdeman.cn/zhangdeman/trace"
"git.zhangdeman.cn/zhangdeman/wrapper"
"github.com/gin-gonic/gin"
"os"
"strings"
"sync"
"time"
)
// getTraceID 生成traceID
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 23:12 2022/6/25
func getTraceID(ctx *gin.Context) string {
hostname, _ := os.Hostname()
if hostname != "" {
hostname = "unknown"
}
return fmt.Sprintf(
"%v-%v-%v-%v-%v",
time.Now().UnixNano()/1e6,
strings.ReplaceAll(networkUtil.IP.GetHostIP(), ".", ""),
strings.ReplaceAll(hostname, ".", ""),
strings.ReplaceAll(networkUtil.IP.GetRemoteIP(ctx.Request), ".", ""),
wrapper.StringFromRandom(32, "").Md5().Value,
)
}
// getRequestID 生成requestID
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 23:12 2022/6/25
func getRequestID(ctx *gin.Context, traceID string) string {
requestID := ctx.GetHeader("X-Forward-Request-Id")
if len(requestID) > 0 {
return requestID
}
if len(traceID) > 0 {
return traceID
}
return getTraceID(ctx)
}
// NewContext 获取context实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:01 2025/2/28
func NewContext(ginContext *gin.Context) *Context {
// 生成traceID
traceID := getRequestID(ginContext, "")
hostname, _ := os.Hostname()
return &Context{
Context: ginContext,
StartRequestTime: time.Now(),
FinishRequestTime: time.Unix(0, 0),
HandlerAfterResponse: make([]gin.HandlerFunc, 0),
customData: make(map[string]any),
Hostname: hostname,
HostIp: networkUtil.IP.GetHostIP(),
customLock: &sync.RWMutex{},
TraceID: traceID,
Trace: trace.NewRuntime(traceID, 0),
}
}
// Context 请求上下文信息, 对 ctx *gin.Context的二次包装
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:45 2025/2/28
type Context struct {
*gin.Context `json:"-"`
StartRequestTime time.Time `json:"start_request_time"` // 开始请求时间(全局)
FinishRequestTime time.Time `json:"finish_request_time"` // 结束请求时间(全局)
TraceID string `json:"trace_id"` // 请求trace_id
ParentAppName string `json:"parent_app_name"` // 父级应用名称
HandlerAfterResponse []gin.HandlerFunc `json:"-"` // 响应数据之后需要执行的逻辑
Hostname string `json:"hostname"` // 服务器名称
HostIp string `json:"host_ip"` // 服务器IP
customData map[string]any `json:"-"` // 业务上下文自定义数据, 可以合 ginCtx.Value 作区分
customLock *sync.RWMutex `json:"-"` // 数据锁
Trace *trace.Runtime `json:"-"` // 追踪实例
}
// SetCustom 设置数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:58 2025/2/28
func (ctx *Context) SetCustom(key string, val any) {
ctx.customLock.Lock()
defer ctx.customLock.Unlock()
if nil == ctx.customData {
ctx.customData = make(map[string]any)
}
ctx.Set(key, val)
ctx.customData[key] = val
}
// customDataValue 获取customData
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:03 2025/2/28
func (ctx *Context) customDataValue(key string) any {
ctx.customLock.RLock()
defer ctx.customLock.RUnlock()
if val, exist := ctx.customData[key]; exist {
return val
} else {
return ctx.Value(key)
}
}
// customDataExist 判断
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:04 2025/2/28
func (ctx *Context) customDataExist(key string) any {
ctx.customLock.RLock()
defer ctx.customLock.RUnlock()
_, exist := ctx.customData[key]
return exist
}
// customDataAppend 向数组数据中追加元素
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:07 2025/2/28
func (ctx *Context) customDataAppend(key string, appendValue any) {
ctx.customLock.Lock()
defer ctx.customLock.Unlock()
val, exist := ctx.customData[key]
if !exist || nil == val {
ctx.customData[key] = []any{appendValue}
return
}
if valArr, ok := val.([]any); ok {
valArr = append(valArr, appendValue)
ctx.customData[key] = valArr
}
// 非数组, 忽略追加行为
}
// customDataAppendProperty 向一个map中追加/更新属性
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:11 2025/2/28
func (ctx *Context) customDataAppendProperty(key string, appendKey string, appendValue any) {
ctx.customLock.Lock()
defer ctx.customLock.Unlock()
val, exist := ctx.customData[key]
if !exist || nil == val {
ctx.customData[key] = map[string]any{
appendKey: appendValue,
}
return
}
if valMap, ok := val.(map[string]any); ok {
valMap[appendKey] = appendValue
ctx.customData[key] = valMap
}
// 非map, 忽略追加行为
}
// TraceStart 开始监控
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:24 2025/2/28
func (ctx *Context) TraceStart(action string, actionData map[string]any) int {
return ctx.Trace.StartBehavior(action, actionData)
}
// TraceEnd 完成监控
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:24 2025/2/28
func (ctx *Context) TraceEnd(behaviorID int, endData map[string]any) {
ctx.Trace.FinishBehavior(behaviorID, endData)
}

View File

@ -10,6 +10,7 @@ package request
import (
"bytes"
"errors"
"github.com/mcuadros/go-defaults"
"io"
"net/http"
"strings"
@ -36,10 +37,10 @@ type form struct {
//
// Date : 00:34 2022/7/3
func (f *form) Parse(ctx *gin.Context, receiver interface{}) error {
handleConfig := define.GetHttpHandleConfig()
requestBody, _ := io.ReadAll(ctx.Request.Body)
// 请求信息写入上下文
ctx.Set(define.RecordRequestDataField, string(requestBody))
ctx.Set(handleConfig.RecordRequestDataField, string(requestBody))
// 因为请求体被读一遍之后就没了,重新赋值 requestBody
ctx.Request.Body = io.NopCloser(bytes.NewReader(requestBody))
method := strings.ToUpper(ctx.Request.Method)
@ -48,21 +49,27 @@ func (f *form) Parse(ctx *gin.Context, receiver interface{}) error {
method == http.MethodTrace ||
method == http.MethodConnect ||
method == http.MethodOptions {
return ctx.ShouldBindQuery(receiver)
}
if method == http.MethodPost ||
if err := ctx.ShouldBindQuery(receiver); nil != err {
return err
}
} else if method == http.MethodPost ||
method == http.MethodPut ||
method == http.MethodDelete {
if ContentType.IsJson(ctx) {
return ctx.ShouldBindJSON(receiver)
if err := ctx.ShouldBindJSON(receiver); nil != err {
return err
}
} else if ContentType.IsFormURLEncoded(ctx) {
if err := ctx.ShouldBind(receiver); nil != err {
return err
}
} else {
return errors.New(ctx.ContentType() + " is not support")
}
if ContentType.IsFormURLEncoded(ctx) {
return ctx.ShouldBind(receiver)
}
return errors.New(ctx.ContentType() + " is not support")
} else {
return errors.New(method + " : request method is not support")
}
return errors.New(method + " : request method is not support")
// 设置默认值
defaults.SetDefaults(receiver)
return nil
}

View File

@ -0,0 +1,46 @@
// Package parse_body ...
//
// Description : parse_body ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 16:51
package parse_body
import (
"bytes"
"github.com/gin-gonic/gin"
"io"
)
type base struct {
}
// Unmarshal 反序列化方法,可以不指定
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:54 2024/10/22
func (b base) Unmarshal() func(sourceData []byte, receiver any) error {
return nil
}
// DoParse 解析
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:08 2024/10/22
func (b base) DoParse(ctx *gin.Context, receiver any, unmarshalFunc func(sourceData []byte, receiver any) error) ([]byte, error) {
data, err := io.ReadAll(ctx.Request.Body)
if nil != err {
return nil, err
}
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(data))
if nil == unmarshalFunc || nil == receiver {
return data, nil
}
if err := unmarshalFunc(data, receiver); nil != err {
return nil, err
}
return data, err
}

View File

@ -0,0 +1,70 @@
// Package parse_body ...
//
// Description : parse_body ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 16:43
package parse_body
import (
"errors"
"git.zhangdeman.cn/zhangdeman/gin/request/abstract"
"github.com/gin-gonic/gin"
"strings"
)
var (
requestBodyParseAdaptorTable = map[string]abstract.RequestBodyParseAdaptor{}
)
func init() {
adaptorList := []abstract.RequestBodyParseAdaptor{
JsonAdaptor{},
FormUrlEncode{},
XmlAdaptor{},
}
for _, itemAdaptor := range adaptorList {
Register(itemAdaptor)
}
}
// Register 注册适配器实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:45 2024/10/22
func Register(adaptor abstract.RequestBodyParseAdaptor) {
if nil == adaptor {
return
}
requestBodyParseAdaptorTable[adaptor.ContentType()] = adaptor
}
// Execute 解析请求BODY数据
func Execute(ctx *gin.Context, receiver any) ([]byte, error) {
contentType := strings.ToLower(strings.ReplaceAll(ctx.ContentType(), " ", ""))
// 裁剪出真实的类型,之所以截取,是因为 content_type 中可能还包含编码信息, 如 : application/json;charset=utf8
contentTypeArr := strings.Split(contentType, ";")
contentType = contentTypeArr[0]
if _, exist := requestBodyParseAdaptorTable[contentType]; !exist {
return nil, errors.New(contentType + " : adaptor not found")
}
if parseResult, err := requestBodyParseAdaptorTable[contentType].Parse(ctx, receiver); nil != err {
return nil, err
} else {
return parseResult, err
}
}
// ExecuteForMap 高层级包装表单解析为map
func ExecuteForMap(ctx *gin.Context) (map[string]any, error) {
var (
err error
result map[string]any
)
if _, err = Execute(ctx, &result); nil != err {
return nil, err
}
return result, nil
}

View File

@ -0,0 +1,44 @@
// Package parse_body ...
//
// Description : parse_body ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 16:43
package parse_body
import (
"encoding/json"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/serialize"
"github.com/gin-gonic/gin"
)
type FormUrlEncode struct {
base
}
func (f FormUrlEncode) Parse(ctx *gin.Context, receiver any) ([]byte, error) {
if err := ctx.Request.ParseForm(); nil != err {
return nil, err
}
body := map[string]string{}
for paramName, itemParam := range ctx.Request.PostForm {
if len(itemParam) > 0 {
body[paramName] = itemParam[0]
} else {
body[paramName] = ""
}
}
byteData, _ := json.Marshal(body)
if nil != receiver {
if err := serialize.JSON.UnmarshalWithNumber(byteData, receiver); nil != receiver {
return nil, err
}
}
return byteData, nil
}
func (f FormUrlEncode) ContentType() string {
return consts.MimeTypeXWWWFormUrlencoded
}

View File

@ -0,0 +1,49 @@
// Package parse_body ...
//
// Description : parse_body ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 16:42
package parse_body
import (
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/serialize"
"github.com/gin-gonic/gin"
)
type JsonAdaptor struct {
base
}
// Parse 解析json请求体
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:13 2024/10/22
func (j JsonAdaptor) Parse(ctx *gin.Context, receiver any) ([]byte, error) {
unmarshalFunc := j.Unmarshal()
if nil == unmarshalFunc {
unmarshalFunc = serialize.JSON.UnmarshalWithNumber
}
return j.DoParse(ctx, receiver, unmarshalFunc)
}
// Unmarshal 指定解析方法
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:56 2024/10/22
func (j JsonAdaptor) Unmarshal() func(sourceData []byte, receiver any) error {
return serialize.JSON.UnmarshalWithNumber
}
// ContentType 请求类型固定返回json
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:59 2024/10/22
func (j JsonAdaptor) ContentType() string {
return consts.MimeTypeJson
}

42
request/parse_body/xml.go Normal file
View File

@ -0,0 +1,42 @@
// Package parse_body ...
//
// Description : parse_body ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 16:42
package parse_body
import (
"bytes"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/serialize"
"github.com/gin-gonic/gin"
"github.com/sbabiv/xml2map"
)
type XmlAdaptor struct {
base
}
func (x XmlAdaptor) Parse(ctx *gin.Context, receiver any) ([]byte, error) {
unmarshalFunc := x.Unmarshal()
if nil == unmarshalFunc {
unmarshalFunc = serialize.Xml.UnmarshalWithNumber
}
return x.DoParse(ctx, receiver, unmarshalFunc)
}
func (x XmlAdaptor) Unmarshal() func(sourceData []byte, receiver any) error {
return func(sourceData []byte, receiver any) error {
res, err := xml2map.NewDecoder(bytes.NewReader(sourceData)).Decode()
if nil != err {
return err
}
return serialize.JSON.Transition(res, receiver)
}
}
func (x XmlAdaptor) ContentType() string {
return consts.MimeTypeXml
}

View File

@ -0,0 +1,8 @@
// Package parse_body ...
//
// Description : parse_body ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-10-22 16:43
package parse_body

View File

@ -1,4 +1,4 @@
// Package gin ...
// Package request ...
//
// Description : gin ...
//
@ -8,10 +8,12 @@
package request
import (
"git.zhangdeman.cn/zhangdeman/gin/define"
"git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
"git.zhangdeman.cn/zhangdeman/wrapper"
"github.com/gin-gonic/gin"
"net/url"
"strings"
"sync"
)
var (
@ -74,16 +76,24 @@ func (wh *wrapperHandle) GetScheme(ctx *gin.Context, defaultVal string) string {
return defaultVal
}
// GetQuery 获取query参数
// ParseQuery 获取query参数
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:03 2024/1/2
func (wh *wrapperHandle) GetQuery(ctx *gin.Context) url.Values {
func (wh *wrapperHandle) ParseQuery(ctx *gin.Context) map[string]string {
query := make(map[string]string)
if nil != ctx && nil != ctx.Request && nil != ctx.Request.URL {
return ctx.Request.URL.Query()
for paramName, valueList := range ctx.Request.URL.Query() {
if len(valueList) == 0 {
query[paramName] = ""
} else {
query[paramName] = valueList[0]
}
}
return query
}
return make(url.Values)
return query
}
// GetMethod 获取请求方法
@ -107,8 +117,34 @@ func (wh *wrapperHandle) GetContentType(ctx *gin.Context, defaultVal string) str
if nil == ctx {
return defaultVal
}
contentType := strings.ToLower(ctx.ContentType())
return wrapper.TernaryOperator.String(len(contentType) > 0, wrapper.String(contentType), wrapper.String(defaultVal)).Value()
contentTypeVal := strings.ToLower(ctx.ContentType())
return wrapper.TernaryOperator.String(len(contentTypeVal) > 0, wrapper.String(contentTypeVal), wrapper.String(defaultVal)).Value()
}
// GetDomain 获取请求Domain
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:12 2024/9/19
func (wh *wrapperHandle) GetDomain(ctx *gin.Context) string {
if nil == ctx {
return ""
}
return ctx.Request.Host
}
// ParseBody 获取请求body
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:25 2024/7/26
func (wh *wrapperHandle) ParseBody(ctx *gin.Context) map[string]any {
body := map[string]any{}
if _, err := parse_body.Execute(ctx, &body); nil != err {
return map[string]any{}
}
return body
}
// GetResponseBody 获取响应body
@ -116,6 +152,195 @@ func (wh *wrapperHandle) GetContentType(ctx *gin.Context, defaultVal string) str
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:18 2024/1/2
func (wh *wrapperHandle) GetResponseBody(ctx *gin.Context, defaultVal string) string {
return ""
func (wh *wrapperHandle) GetResponseBody(ctx *gin.Context, key string, defaultVal any) any {
if nil == ctx {
return defaultVal
}
if val, exist := ctx.Get(key); !exist || nil == val {
return defaultVal
} else {
return val
}
}
// GetClientIp 获取请求客户端IP
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:31 2024/7/23
func (wh *wrapperHandle) GetClientIp(ctx *gin.Context, defaultVal string) string {
if nil == ctx {
return defaultVal
}
return ctx.ClientIP()
}
// GetUserAgent 获取user_agent
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:32 2024/7/23
func (wh *wrapperHandle) GetUserAgent(ctx *gin.Context, defaultVal string) string {
if nil == ctx {
return defaultVal
}
return ctx.Request.UserAgent()
}
// GetCtxData 获取请求上下文数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:35 2024/7/23
func (wh *wrapperHandle) GetCtxData(ctx *gin.Context, key string, defaultVal any) any {
if nil == ctx {
return defaultVal
}
if val, exist := ctx.Get(key); !exist || nil == val {
return defaultVal
} else {
return val
}
}
// GetCtxStringData 获取字符串数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:37 2024/7/23
func (wh *wrapperHandle) GetCtxStringData(ctx *gin.Context, key string, defaultVal string) string {
if nil == ctx {
return defaultVal
}
val := ctx.GetString(key)
if len(val) == 0 {
return defaultVal
}
return val
}
// GetCtxIntData 获取int数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:39 2024/7/23
func (wh *wrapperHandle) GetCtxIntData(ctx *gin.Context, key string, defaultVal int64) int64 {
if nil == ctx {
return defaultVal
}
val := wh.GetCtxData(ctx, key, nil)
if nil == val {
return defaultVal
}
return ctx.GetInt64(key)
}
// ParseHeader 解析header数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:04 2024/9/23
func (wh *wrapperHandle) ParseHeader(ctx *gin.Context) map[string]string {
headerData := map[string]string{}
if nil == ctx || nil == ctx.Request {
return headerData
}
for headerName, headerVal := range ctx.Request.Header {
if len(headerVal) > 0 {
headerData[headerName] = headerVal[0]
} else {
headerData[headerName] = ""
}
}
return headerData
}
// ParseCookie 解析cookie数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:06 2024/9/23
func (wh *wrapperHandle) ParseCookie(ctx *gin.Context) map[string]string {
cookieData := map[string]string{}
if nil == ctx || nil == ctx.Request {
return cookieData
}
for _, itemCookie := range ctx.Request.Cookies() {
cookieData[itemCookie.Name] = itemCookie.Value
}
return cookieData
}
// Copy 复制context
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:00 2025/2/28
//
// - ctx *gin.Context 基于哪一个context复制context
// - isCopyContextValue bool : 是否继承父级ctx的自定义value
func (wh *wrapperHandle) Copy(ctx *gin.Context, isCopyContextValue bool, excludeKeyList []string) *gin.Context {
if nil == ctx {
return nil
}
newContext := ctx.Copy()
excludeKeyTable := map[string]bool{}
for _, excludeKey := range excludeKeyList {
excludeKeyTable[excludeKey] = true
}
if isCopyContextValue {
// 上下文设置的业务数据值也继承下来, TODO: 并发读写此处可能panic
for k, v := range newContext.Keys {
if !excludeKeyTable[k] {
// 没有指定不继承
ctx.Set(k, v)
}
}
}
// 设置父级context
ctx.Set("parent_context", ctx)
if nil == ctx.Value("root_context") {
ctx.Set("root_context", ctx)
}
return newContext
}
// GetLogicAfterResponse ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:54 2025/2/28
func (wh *wrapperHandle) GetLogicAfterResponse(ctx *gin.Context) *define.LogicAfterResponse {
if nil == ctx || nil == ctx.Request {
return nil
}
l, exist := ctx.Get(define.LogicAfterResponseKey)
if !exist || nil == l {
l = &define.LogicAfterResponse{
SuccessHookFuncList: make([]func(), 0),
FailureHookFuncList: make([]func(), 0),
Lock: &sync.RWMutex{},
}
ctx.Set(define.LogicAfterResponseKey, l)
}
// 就这么写, key值如果被其他人覆盖成非法值, 此处会直接panic
return l.(*define.LogicAfterResponse)
}
// GetGinWrapperContext 获取包装之后context
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:41 2025/3/10
func (wh *wrapperHandle) GetGinWrapperContext(ctx *gin.Context) *Context {
if nil == ctx {
return nil
}
if val, exist := ctx.Get(define.GinWrapperContextKey); !exist || nil == val {
newCtx := NewContext(ctx)
newCtx.SetCustom(define.GinWrapperContextKey, newCtx)
return newCtx
} else {
return val.(*Context)
}
}

View File

@ -8,6 +8,7 @@
package response
import (
"fmt"
"net/http"
"time"
@ -21,14 +22,38 @@ const (
hasSendResponseFlag = "GIN_PKG_HAS_SEND_RESPONSE"
)
var (
// 成功的业务状态码
successBusinessCode any = "200"
)
// SetBusinessSuccessCode 设置成状态码
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:01 2024/8/17
func SetBusinessSuccessCode(code any) {
successBusinessCode = code
}
// Success 成功的响应
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:37 2022/6/25
func Success(ctx *gin.Context, data interface{}) {
func Success(ctx *gin.Context, data any) {
successException := exception.NewSuccess(data)
Send(ctx, successException.GetCode(), successException.GetHttpCode(), successException.GetData())
Send(ctx, successException.GetCode(), successException.GetHttpCode(), successException.GetData(), nil)
}
// SuccessWithExtension 返回扩展数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:52 2024/9/24
func SuccessWithExtension(ctx *gin.Context, data any, extension map[string]any) {
successException := exception.NewSuccess(data)
Send(ctx, successException.GetCode(), successException.GetHttpCode(), successException.GetData(), extension)
}
// Send 基础的发送数据
@ -36,25 +61,30 @@ func Success(ctx *gin.Context, data interface{}) {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:40 2022/6/25
func Send(ctx *gin.Context, code interface{}, httpCode int, data interface{}) {
func Send(ctx *gin.Context, code any, httpCode int, data any, extension map[string]any) {
// 设置请求是否成功的标识
ctx.Set(define.GetHttpHandleConfig().RequestIsSuccessField, fmt.Sprintf("%v", code) == fmt.Sprintf("%v", successBusinessCode))
if ctx.GetBool(hasSendResponseFlag) {
// 已经发送过数据, 后面在发送数据, 不执行
return
}
finishRequestTime := time.Now().UnixMilli()
// 设置数据已发送的标识
defer ctx.Set(hasSendResponseFlag, true)
finishRequestTime := time.Now()
responseData := map[string]interface{}{
define.ResponseCodeField: code,
define.ResponseMessageField: exception.GetMessage(code),
define.ResponseTraceIDField: ctx.GetString(define.TraceIDField),
define.ResponseRequestIDField: ctx.GetString(define.RequestIDField),
define.ResponseDataField: data,
define.HandleRequestCostField: finishRequestTime.UnixMilli() - ctx.GetTime(define.StartRequestTimeField).UnixMilli(),
responseConfig := define.GetHttpHandleConfig()
responseData := map[string]any{
responseConfig.ResponseCodeField: code,
responseConfig.ResponseMessageField: exception.GetMessage(code),
responseConfig.ResponseTraceIDField: ctx.GetString(responseConfig.ResponseTraceIDField),
responseConfig.ResponseDataField: data,
responseConfig.HandleRequestCostField: finishRequestTime - ctx.GetInt64(responseConfig.StartRequestTimeField),
}
if responseConfig.EnableExtensionOutput && nil != extension {
responseData[responseConfig.ExtensionOutputField] = extension
}
// 记录完成时间
ctx.Set(define.FinishRequestTimeField, finishRequestTime)
ctx.Set(define.RecordResponseDataField, responseData)
ctx.Set(responseConfig.FinishRequestTimeField, finishRequestTime)
ctx.Set(responseConfig.ResponseDataField, responseData)
responseException := exception.New(code, httpCode, responseData)
ctx.JSON(responseException.GetHttpCode(), responseException.GetData())
}
@ -64,8 +94,8 @@ func Send(ctx *gin.Context, code interface{}, httpCode int, data interface{}) {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:41 2022/6/25
func SendWithStatusOK(ctx *gin.Context, code interface{}, data interface{}) {
Send(ctx, code, http.StatusOK, data)
func SendWithStatusOK(ctx *gin.Context, code any, data any) {
Send(ctx, code, http.StatusOK, data, nil)
}
// SendWithException 使用exception发送数据
@ -73,15 +103,21 @@ func SendWithStatusOK(ctx *gin.Context, code interface{}, data interface{}) {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 13:08 2022/6/26
func SendWithException(ctx *gin.Context, e exception.IException, data interface{}) {
func SendWithException(ctx *gin.Context, e exception.IException, data map[string]any) {
if nil == e {
e = exception.NewSuccess(data)
}
outputData := map[string]interface{}{
if !define.GetHttpHandleConfig().DisableDebugStackOutput && nil != e {
stack := e.GetStack()
if len(stack) > 0 {
fmt.Println(stack)
}
}
outputData := map[string]any{
"e_data": e.GetData(),
"u_e_data": data,
}
Send(ctx, e.GetCode(), e.GetHttpCode(), outputData)
Send(ctx, e.GetCode(), e.GetHttpCode(), outputData, nil)
}
// JSON ctx.JSON 的平替, 增加了数据是否已相应的标识
@ -89,7 +125,7 @@ func SendWithException(ctx *gin.Context, e exception.IException, data interface{
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:51 2023/2/15
func JSON(ctx *gin.Context, httpCode int, data interface{}) {
func JSON(ctx *gin.Context, httpCode int, data any) {
if ctx.GetBool(hasSendResponseFlag) {
// 已经发送过数据, 后面在发送数据, 不执行
return

25
router/config.go Normal file
View File

@ -0,0 +1,25 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-07 17:41
package router
import "strings"
var defaultValidateErrTag = "err"
// SetValidateErrTag 设置验证失败时, 获取错误信息的tag字段
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:42 2025/2/7
func SetValidateErrTag(tagName string) {
tagName = strings.TrimSpace(tagName)
if tagName == "" {
return
}
defaultValidateErrTag = tagName
}

153
router/controller.go Normal file
View File

@ -0,0 +1,153 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-01-27 15:29
package router
import (
"reflect"
"strings"
)
// controller 解析controller有哪些方法要注册为接口
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:30 2025/1/27
type controller struct {
}
// Parse 执行解析
//
// 符合注册为借口的方法具有如下特征:
//
// 1. 函数接受两个入参, 第一个参数为 gin.Context , 第二个参数为 任意结构体指针, 但是必须声明 Meta 相关信息, 否则会报错
//
// 2. 函数有两个返回值, 第一个返回值为任意结构体/结构体指针(限制死不能为map/slice, 方便后续统一标准化) , 第二个返回值为 error , 代表处理的异常, 会自动适配 exception.IException 类型
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:31 2025/1/27
func (c controller) Parse(inputController any) map[string]UriConfig {
parseRes := make(map[string]UriConfig)
if nil == inputController {
return parseRes
}
controllerType := reflect.TypeOf(inputController)
inputController = reflect.New(controllerType).Interface()
controllerType = reflect.TypeOf(inputController)
controllerValue := reflect.ValueOf(inputController)
for methodIdx := 0; methodIdx < controllerType.NumMethod(); methodIdx++ {
uriCfg, needRegister := c.methodConfig(controllerType.Method(methodIdx))
if !needRegister {
continue
}
uriCfg.ApiStructValue = controllerValue
parseRes[uriCfg.Path] = uriCfg
}
return parseRes
}
// methodConfig 解析方法配置, 要求函数格式, 两个参数, 两个返回值, 格式 : func(ctx *gin.Context, formData anyStruct[组合Meta]) (anyStruct|map[response], error)
//
// 参数 : 方法反射结果
//
// 返回值 : 第一个 -> 解析出的接口配置 第二个 -> 是否要注册为接口
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:05 2025/1/27
func (c controller) methodConfig(reflectMethod reflect.Method) (cfg UriConfig, needRegister bool) {
methodType := reflectMethod.Type
// num0: 函数声明
// num1: 第一个参数
// num2: 第二个参数
if methodType.NumIn() != 3 {
needRegister = false
return
}
// 第一个参数必须是 *gin.Context
firstParamStr := methodType.In(1).String()
if firstParamStr != GinContextType && firstParamStr != GinWrapperContextType {
needRegister = false
return
}
// 解析第二个参数是组合Meta的form表单
formType := methodType.In(2)
cfg.FormDataType = formType
if formType.Kind() == reflect.Ptr {
formType = methodType.In(2).Elem()
}
metaField, metaFieldExist := formType.FieldByName(FieldNameMeta)
if !metaFieldExist {
needRegister = false
return
}
cfg.ResultDataType = methodType.Out(0)
if cfg.ResultDataType == nil {
}
if methodType.Out(1).Kind().String() != ErrorType {
// 判断是否是实现 error接口的方法
outputErrParse := false
for j := 0; j < methodType.Out(1).NumMethod(); j++ {
if methodType.Out(1).Method(j).Name == ErrorInterfaceFuncName && // 实现Error方法
methodType.Out(1).Method(j).Type.NumIn() == 0 && // 没有任何参数
methodType.Out(1).Method(j).Type.NumOut() == 1 && // 一个返回值
methodType.Out(1).Method(j).Type.Out(0).Kind().String() == reflect.String.String() {
outputErrParse = true
break
}
}
if !outputErrParse {
needRegister = false
return
}
}
// 是否gin包装后的context
cfg.IsGinWrapperContext = firstParamStr == GinWrapperContextType
// 解析meta信息
cfg.Path = metaField.Tag.Get(TagNamePath)
cfg.RequestMethod = metaField.Tag.Get(TagNameMethod)
cfg.Desc = metaField.Tag.Get(TagNameDesc)
cfg.TagList = strings.Split(metaField.Tag.Get(TagNameUriTag), ",")
// 解析第一个返回值, 要求必须是结构体或者是map
outputStrictModel := metaField.Tag.Get(TagNameOutputStrict)
cfg.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
if cfg.OutputStrict {
// 开启输出严格模式校验
if methodType.Out(0).Kind() != reflect.Struct && methodType.Out(0).Kind() != reflect.Map {
panic(cfg.Path + " : 接口配置输出严格校验, 输出数据类型必须为 struct 或 *struct 或 map, 实际返回数据类型 : " + methodType.Out(0).Kind().String())
return
}
}
// 解析参数配置
//cfg.ParamList = c.parseParamConfig(formType)
cfg.ApiLogicFunc = reflectMethod
needRegister = true
return
}
// parseParamConfig 解析参数配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:35 2025/2/7
func (c controller) parseParamConfig(formDataType reflect.Type) []UriParam {
res := make([]UriParam, 0)
for i := 0; i < formDataType.NumField(); i++ {
structField := formDataType.Field(i)
if structField.Name == FieldNameMeta {
// Meta 字段, 忽略
continue
}
jsonTag := structField.Tag.Get("json")
if jsonTag == "" {
jsonTag = structField.Name
}
}
return res
}

View File

@ -7,19 +7,28 @@
// Date : 2024-07-20 22:57
package router
import "reflect"
const (
PrefixFuncName = "RouterPrefix" // 路由前缀函数名称
MiddlewareFuncName = "RouterMiddleware" // 路由中间件函数名称
import (
"reflect"
)
const (
TagNamePath = "path" // 接口的请求路径
TagNameMethod = "method" // 接口的请求方法
TagNameUriTag = "tag" // 接口的tag
TagNameDesc = "desc" // 接口的描述
TagNameStrict = "strict" // 接口是否为严格模式 : 不配置, 则为严格模式.严格模式 : POST 仅解析 BODY , GET 仅解析 QUERY
PrefixFuncName = "RouterPrefix" // 路由前缀函数名称
MiddlewareFuncName = "RouterMiddleware" // 路由中间件函数名称
GinContextType = "*gin.Context" // gin context 类型名称
GinWrapperContextType = "*request.Context" // gin context 类型名称
ErrorType = "error" // error类型
ErrorInterfaceFuncName = "Error" // error接口需要实现的方法名称
)
const (
TagNamePath = "path" // 接口的请求路径
TagNameMethod = "method" // 接口的请求方法
TagNameUriTag = "tag" // 接口的tag
TagNameDesc = "desc" // 接口的描述
TagNameOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map非严格模式返回任意值
TagNameBinding = "binding" // gin 内置的验证规则tag
TagNameValidate = "validate" // validator v10 默认的验证规则tag
TagNameErrMsg = "err" // 验证失败错误信息tag
)
// UriConfig 接口配置
@ -28,10 +37,33 @@ const (
//
// Date : 15:41 2024/7/21
type UriConfig struct {
Path string `json:"path"` // 接口路由, 必须配置
Method string `json:"method"` // 接口请求方法, 必须配置
TagList []string `json:"tag_list"` // 接口分组
Desc string `json:"desc"` // 接口描述
Strict bool `json:"strict"` // 接口是否为严格模式 : 不配置, 则为严格模式.严格模式 : POST 仅解析 BODY , GET 仅解析 QUERY
FormDataType reflect.Type `json:"-"` // 表单数据类型
Path string `json:"path"` // 接口路由, 必须配置
RequestMethod string `json:"request_method"` // 接口请求方法, 必须配置
TagList []string `json:"tag_list"` // 接口分组
Desc string `json:"desc"` // 接口描述
OutputStrict bool `json:"output_strict"` // 接口是否为严格模式 : 不配置,可返回任意类型, 配置, 必须返回结构体或者map
FormDataType reflect.Type `json:"-"` // 表单数据类型
ResultDataType reflect.Type `json:"-"` // 返回值数据类型
ApiStructValue reflect.Value `json:"-"` // 逻辑函数所属结构体取值
ApiLogicFunc reflect.Method `json:"-"` // 自定义的接口逻辑
IsGinWrapperContext bool `json:"is_gin_wrapper_context"` // 是否gin包装后的context
}
// UriParam 接口参数配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:40 2025/1/27
type UriParam struct {
Field string `json:"field"` // 结构体字段
Name string `json:"name"` // 参数名称
Type string `json:"type"` // 参数类型
Validate string `json:"validate"` // 验证规则: validator/v10 库
ErrorMsg string `json:"error_msg"` // 验证失败的错误信息
DisableAutoType bool `json:"disable_auto_type"` // 禁用自动类型转换
Sort string `json:"sort"` // 参数读取顺序: 默认 POST : body > query > path GET : query > path > body
}
const (
FieldNameMeta = "Meta" // 元信息字段
)

43
router/doc.go Normal file
View File

@ -0,0 +1,43 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-14 21:48
package router
import (
"reflect"
apiDoc "git.zhangdeman.cn/gateway/api-doc"
"git.zhangdeman.cn/gateway/api-doc/define"
)
func NewDoc(info *define.Info, servers []*define.ServerItem) *Doc {
return &Doc{
instance: apiDoc.NewOpenapiDoc(info, servers),
}
}
type Doc struct {
instance *apiDoc.Generate
}
// Add 增加接口文档测试
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:55 2025/2/14
func (d *Doc) Add(routePrefix string, paramType reflect.Type, resultType reflect.Type) {
_ = d.instance.AddApiFromInAndOut(routePrefix, paramType, resultType)
}
// Data 文档数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:59 2025/2/14
func (d *Doc) Data() *define.OpenapiDoc {
return d.instance.Doc()
}

109
router/handler.go Normal file
View File

@ -0,0 +1,109 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-01-27 19:42
package router
import (
"git.zhangdeman.cn/zhangdeman/exception"
"git.zhangdeman.cn/zhangdeman/gin/define"
"git.zhangdeman.cn/zhangdeman/gin/request"
"git.zhangdeman.cn/zhangdeman/gin/response"
"github.com/gin-gonic/gin"
"reflect"
"sync"
)
// RequestHandler 获取请求处理方法
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:44 2025/1/27
func RequestHandler(uriCfg UriConfig) gin.HandlerFunc {
return func(ctx *gin.Context) {
var (
err error
ok bool
e exception.IException
)
var formParam reflect.Value
if uriCfg.FormDataType.Kind() == reflect.Ptr {
formParam = reflect.New(uriCfg.FormDataType.Elem())
} else {
// 表单解析
formParam = reflect.New(uriCfg.FormDataType)
}
formValue := formParam.Interface()
if err = request.Form.Parse(ctx, formValue); nil != err {
// 格式化验证错误的信息
err = GetValidateErr(formValue, err)
e = exception.NewFromError(400, err)
response.SendWithException(ctx, e, nil)
ctx.Abort()
return
}
isSuccess := false
// 初始化响应之后logic
logicAfterResponse := &define.LogicAfterResponse{
SuccessHookFuncList: make([]func(), 0),
FailureHookFuncList: make([]func(), 0),
Lock: &sync.RWMutex{},
}
// 此处暴露出去,是为了使用方可以获取到对应数据
ctx.Set(define.LogicAfterResponseKey, logicAfterResponse)
defer func() {
go func() {
defer recover()
if isSuccess {
for _, itemFunc := range logicAfterResponse.SuccessHookFuncList {
if nil != itemFunc {
itemFunc()
}
}
} else {
for _, itemFunc := range logicAfterResponse.FailureHookFuncList {
if nil != itemFunc {
itemFunc()
}
}
}
}()
}()
// 执行逻辑
inputValue := reflect.ValueOf(formValue)
if uriCfg.FormDataType.Kind() != reflect.Ptr {
inputValue = inputValue.Elem()
}
ginWrapperContext := request.NewContext(ctx)
// wrapper context 设置到 gin context
ginWrapperContext.Set(define.GinWrapperContextKey, ginWrapperContext)
ctxReflect := reflect.ValueOf(ctx)
if uriCfg.IsGinWrapperContext {
ctxReflect = reflect.ValueOf(ginWrapperContext)
}
resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, ctxReflect, inputValue})
if resList[1].IsNil() {
// 请求成功
isSuccess = true
response.Success(ctx, resList[0].Interface())
return
}
// 请求失败
if e, ok = resList[1].Interface().(exception.IException); ok {
// 本身就是exception.IException
} else if err, ok = resList[1].Interface().(error); ok {
e = exception.NewFromError(-1, err)
} else {
e = exception.NewWithCodeAndData(-1, map[string]any{
"err": resList[1].Interface(),
})
}
response.SendWithException(ctx, e, nil)
return
}
}

View File

@ -7,7 +7,11 @@
// Date : 2024-07-20 21:40
package router
// Meta 接口的元信息
// Meta 接口的元信息, 主要包含如下信息:
//
// uri: 接口路由(不包含group前缀)
//
// method: 请求方法: get/post 等
//
// Author : go_developer@163.com<白茶清欢>
//

162
router/option.go Normal file
View File

@ -0,0 +1,162 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-18 17:26
package router
import (
apiDocDefine "git.zhangdeman.cn/gateway/api-doc/define"
"git.zhangdeman.cn/zhangdeman/gin/middleware"
"github.com/gin-gonic/gin"
"strings"
)
type SetServerOptionFunc func(so *serverOption)
// serverOption 获取server实例的选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:28 2025/2/18
type serverOption struct {
swaggerUiTheme string // swagger 主题
swaggerBaseUri string // swagger基础path
globalMiddlewareList []gin.HandlerFunc // 全局中间件列表
disableSwaggerDoc bool // 禁用swagger文档, 特定环境不想展示文档, 可通过次方式禁用
serverInfo *apiDocDefine.Info // 服务器信息
serverList []*apiDocDefine.ServerItem // 服务器环境列表
enablePprof bool // 启用pprof
enableRequestInit bool // 初始化请求,生成trace_id 设置请求时间等
enableCors bool // 启动跨域支持
loggerCfg *middleware.AccessConfig // 日志配置
}
// WithSwaggerUITheme 设置swaggerUI主题
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:29 2025/2/18
func WithSwaggerUITheme(uiTheme string) SetServerOptionFunc {
return func(so *serverOption) {
uiTheme = strings.TrimSpace(uiTheme)
if len(uiTheme) == 0 {
return
}
so.swaggerUiTheme = uiTheme
}
}
// WithGlobalMiddlewareList 设置全局中间件
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:33 2025/2/18
func WithGlobalMiddlewareList(middlewareList ...gin.HandlerFunc) SetServerOptionFunc {
return func(so *serverOption) {
so.globalMiddlewareList = middlewareList
}
}
// WithSwaggerBaseUri ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:05 2025/2/18
func WithSwaggerBaseUri(baseUri string) SetServerOptionFunc {
return func(so *serverOption) {
baseUri = strings.TrimSpace(baseUri)
if len(baseUri) == 0 {
return
}
baseUri = "/" + strings.TrimLeft(baseUri, "/")
so.swaggerBaseUri = baseUri
}
}
// WithDisableSwaggerDoc 禁用swagger文档
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:35 2025/2/18
func WithDisableSwaggerDoc() SetServerOptionFunc {
return func(so *serverOption) {
so.disableSwaggerDoc = true
}
}
// WithServerInfo 设置serverInfo
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:51 2025/2/18
func WithServerInfo(serverInfo *apiDocDefine.Info) SetServerOptionFunc {
return func(so *serverOption) {
if nil == serverInfo {
return
}
so.serverInfo = serverInfo
}
}
// WithServerList 设置服务器列表
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:52 2025/2/18
func WithServerList(serverList []*apiDocDefine.ServerItem) SetServerOptionFunc {
return func(so *serverOption) {
if len(serverList) == 0 {
return
}
so.serverList = serverList
}
}
// WithPprofEnable 启用pprof
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:10 2025/2/21
func WithPprofEnable() SetServerOptionFunc {
return func(so *serverOption) {
so.enablePprof = true
}
}
// WithEnableRequestInit 全局配置初始化
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:56 2025/2/22
func WithEnableRequestInit() SetServerOptionFunc {
return func(so *serverOption) {
so.enableRequestInit = true
}
}
// WithEnableCors 启用全局跨域
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:56 2025/2/22
func WithEnableCors() SetServerOptionFunc {
return func(so *serverOption) {
so.enableCors = true
}
}
// WithLoggerCfg ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:25 2025/2/22
func WithLoggerCfg(loggerCfg *middleware.AccessConfig) SetServerOptionFunc {
return func(so *serverOption) {
if nil != loggerCfg || nil != loggerCfg.Logger {
so.loggerCfg = loggerCfg
}
}
}

View File

@ -35,12 +35,12 @@ func init() {
//
// Date : 21:40 2024/7/20
func Register(port int, controllerList ...any) error {
for _, controller := range controllerList {
if nil == controller {
for _, itemController := range controllerList {
if nil == itemController {
// 忽略空指针
continue
}
parseController(controller)
parseController(itemController)
}
return ginRouter.Run(fmt.Sprintf(":%d", port))
}
@ -109,8 +109,9 @@ func parseUriConfig(methodType reflect.Type, routerPrefix string) (*UriConfig, e
}
// 接口logic共计两个参数. 两个返回值, 格式 : func(ctx *gin.Context, formData any[组合Meta]) (any[response], error)
// 解析第一个参数是 *gin.Context
if methodType.In(1).String() != "*gin.Context" {
// 解析第一个参数是 *gin.Context / 或者包装后的 request.Context
firstParamStr := methodType.In(1).String()
if firstParamStr != "*gin.Context" && firstParamStr != "*request.Context" {
return nil, nil
}
// 解析第二个参数是组合Meta的form表单
@ -118,17 +119,17 @@ func parseUriConfig(methodType reflect.Type, routerPrefix string) (*UriConfig, e
if formType.Kind() == reflect.Ptr {
formType = methodType.In(2).Elem()
}
metaField, metaFieldExist := formType.FieldByName("Meta")
metaField, metaFieldExist := formType.FieldByName(FieldNameMeta)
if !metaFieldExist {
return nil, nil
}
uriConfig := &UriConfig{
Path: strings.TrimRight(routerPrefix, "/") + "/" + strings.TrimLeft(metaField.Tag.Get(TagNamePath), "/"),
Method: strings.ToUpper(metaField.Tag.Get(TagNameMethod)),
TagList: strings.Split(metaField.Tag.Get(TagNameUriTag), "|"),
Desc: metaField.Tag.Get(TagNameDesc),
Strict: wrapper.ArrayType([]string{"", "true"}).Has(strings.ToLower(metaField.Tag.Get(TagNameStrict))) >= 0,
FormDataType: methodType.In(2).Elem(),
Path: strings.TrimRight(routerPrefix, "/") + "/" + strings.TrimLeft(metaField.Tag.Get(TagNamePath), "/"),
RequestMethod: strings.ToUpper(metaField.Tag.Get(TagNameMethod)),
TagList: strings.Split(metaField.Tag.Get(TagNameUriTag), "|"),
Desc: metaField.Tag.Get(TagNameDesc),
OutputStrict: wrapper.ArrayType([]string{"", "true"}).Has(strings.ToLower(metaField.Tag.Get(TagNameOutputStrict))) >= 0,
FormDataType: methodType.In(2).Elem(),
}
// 校验 FormDataType
for fieldIdx := 0; fieldIdx < uriConfig.FormDataType.NumField(); fieldIdx++ {
@ -163,10 +164,10 @@ func registerUri(uriConfig *UriConfig, methodValue reflect.Value, middlewareList
}
err := errData.Interface()
if e, ok := err.(exception.IException); ok {
response.SendWithException(ctx, e, businessData)
response.SendWithException(ctx, e, map[string]any{"business_data": businessData})
return
} else {
response.SendWithException(ctx, exception.NewFromError(-1, errData.Interface().(error)), businessData)
response.SendWithException(ctx, exception.NewFromError(-1, errData.Interface().(error)), map[string]any{"business_data": businessData})
return
}
}
@ -174,7 +175,7 @@ func registerUri(uriConfig *UriConfig, methodValue reflect.Value, middlewareList
middlewareList = append([]gin.HandlerFunc{
middleware.InitRequest(),
}, middlewareList...)
switch uriConfig.Method {
switch uriConfig.RequestMethod {
case http.MethodGet:
ginRouter.GET(uriConfig.Path, middlewareList...)
case http.MethodHead:
@ -196,7 +197,7 @@ func registerUri(uriConfig *UriConfig, methodValue reflect.Value, middlewareList
case "ANY":
ginRouter.Any(uriConfig.Path, middlewareList...)
default:
panic(uriConfig.Path + " : " + uriConfig.Method + " is not support")
panic(uriConfig.Path + " : " + uriConfig.RequestMethod + " is not support")
}
}

View File

@ -8,31 +8,29 @@
package router
import (
"git.zhangdeman.cn/zhangdeman/gin/request"
"testing"
"github.com/gin-gonic/gin"
)
type TestController struct{}
func (t *TestController) RouterPrefix() string {
return "/uri/prefix"
func (t TestController) Logic(ctx *request.Context, formData *TestForm) (TestOut, error) {
return TestOut{
FormData: formData,
}, nil
}
func (t *TestController) RouterMiddleware() []gin.HandlerFunc {
return []gin.HandlerFunc{
func(ctx *gin.Context) {
},
}
}
func (t *TestController) Uri(ctx *gin.Context, formData *TestForm) (any, error) {
return formData, nil
type TestOut struct {
Age int `json:"age" form:"age" binding:"min=20" err_msg:"年龄不能小于20"`
Name string `json:"name" form:"name"`
Test *Test `json:"test" form:"test"`
Num *int64 `json:"num" form:"num"`
FormData *TestForm `json:"form_data" form:"form_data"`
}
type TestForm struct {
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
Age int `json:"age" form:"age"`
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"GET"`
Age int `json:"age" form:"age" binding:"min=20" err_msg:"年龄不能小于20"`
Name string `json:"name" form:"name"`
Test *Test `json:"test" form:"test"`
Num *int64 `json:"num" form:"num"`
@ -42,8 +40,8 @@ type Test struct {
}
func Test_parseController(t *testing.T) {
type args struct {
controller any
}
Register(8080, &TestController{})
SetValidateErrTag("err_msg")
s := NewServer(8888, nil)
s.Group("test", nil, TestController{})
s.Start()
}

205
router/server.go Normal file
View File

@ -0,0 +1,205 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-07 18:19
package router
import (
"fmt"
apiDoc "git.zhangdeman.cn/gateway/api-doc"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/gin/middleware"
"git.zhangdeman.cn/zhangdeman/gin/middleware/request_cors"
"github.com/gin-contrib/pprof"
"net/http"
"strings"
apiDocDefine "git.zhangdeman.cn/gateway/api-doc/define"
"github.com/gin-gonic/gin"
)
func newServerOption(port int, optionList ...SetServerOptionFunc) *serverOption {
option := &serverOption{
swaggerUiTheme: apiDocDefine.SwaggerUIThemeRedocFree,
swaggerBaseUri: "/doc/swagger",
globalMiddlewareList: nil,
disableSwaggerDoc: false,
serverInfo: &apiDocDefine.Info{
Description: "这是一个微服务,提供一些必要的的数据接口功能",
Title: "微服务接口文档",
TermsOfService: "",
Contact: &apiDocDefine.Contact{
Name: "开发人员",
Url: "",
Email: "developer@example.com",
},
License: &apiDocDefine.License{
Name: consts.LicenseApache20,
Url: consts.LicenseUrlTable[consts.LicenseApache20],
},
Version: "0.0.1",
},
serverList: []*apiDocDefine.ServerItem{
{
Url: fmt.Sprintf("http://127.0.0.1:%d", port),
Description: "测试服务器",
Variables: nil,
},
},
}
for _, opt := range optionList {
if nil == opt {
continue
}
opt(option)
}
return option
}
// NewServer server实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:20 2025/2/7
func NewServer(port int, optionList ...SetServerOptionFunc) *server {
if port < 80 {
panic("port should be greater than 80")
}
option := newServerOption(port, optionList...)
globalMiddlewareList := make([]gin.HandlerFunc, 0)
if option.enableRequestInit {
// 初始化请求中间件
globalMiddlewareList = append(globalMiddlewareList, middleware.InitRequest())
}
if nil != option.loggerCfg {
// 请求日志记录中间件
globalMiddlewareList = append(globalMiddlewareList, middleware.LogRequest(option.loggerCfg))
}
if option.enableCors {
// 跨域中间件
globalMiddlewareList = append(globalMiddlewareList, request_cors.New(request_cors.Config{
AllowAllOrigins: true,
AllowOrigins: nil,
AllowOriginFunc: nil,
AllowMethods: []string{"*"},
AllowHeaders: []string{"*"},
AllowCredentials: true,
ExposeHeaders: nil,
MaxAge: 0,
AllowWildcard: true,
AllowBrowserExtensions: true,
AllowWebSockets: true,
AllowFiles: true,
}))
}
if len(option.globalMiddlewareList) > 0 {
// 自定义全局中间件追加
globalMiddlewareList = append(globalMiddlewareList, option.globalMiddlewareList...)
}
r := gin.Default()
// 注册全局中间件
r.Use(globalMiddlewareList...)
// 启用pprof, 注册相关路由
if option.enablePprof {
pprof.Register(r)
}
return &server{
router: r,
uiInstance: apiDoc.NewSwaggerUI(option.serverInfo, option.serverList, option.swaggerUiTheme),
port: port,
option: option,
}
}
type server struct {
router *gin.Engine
port int
uiInstance *apiDoc.SwaggerUI
option *serverOption
}
// Start 启动服务
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:31 2025/2/7
func (s *server) Start() {
// 注册文档
s.uiInstance.RegisterHandler(s.router, s.option.swaggerBaseUri)
if err := s.router.Run(fmt.Sprintf(":%d", s.port)); err != nil {
panic("服务启动监听失败" + err.Error())
}
}
// Router 对外访问路由实例, 不建议直接用
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:35 2025/2/18
func (s *server) Router() *gin.Engine {
return s.router
}
// Handler404 注册404的处理方法
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:01 2025/2/22
func (s *server) Handler404(f gin.HandlerFunc) {
s.router.NoRoute(f)
}
// SetCustomRouter 自定义路由处理
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:09 2025/2/22
func (s *server) SetCustomRouter(f func(r *gin.Engine)) {
if nil == f {
return
}
f(s.router)
}
// Group 注册接口路由
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:35 2025/1/27
func (s *server) Group(routerPrefix string, middlewareList []gin.HandlerFunc, cList ...any) {
g := s.router.Group(routerPrefix)
g.Use(middlewareList...)
cParser := controller{}
for _, c := range cList {
urlTable := cParser.Parse(c)
for _, itemUriCfg := range urlTable {
_ = s.uiInstance.DocInstance().AddApiFromInAndOut(routerPrefix, itemUriCfg.FormDataType, itemUriCfg.ResultDataType)
method := strings.ToUpper(itemUriCfg.RequestMethod)
switch method {
case http.MethodGet:
g.GET(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodHead:
g.HEAD(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodPost:
g.POST(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodPut:
g.PUT(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodPatch:
g.PATCH(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodDelete:
g.DELETE(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodOptions:
g.OPTIONS(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodTrace:
panic(`method Trace is not supported`)
default:
panic("method " + itemUriCfg.RequestMethod + " is not support")
}
}
}
}

55
router/validator.go Normal file
View File

@ -0,0 +1,55 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-07 17:36
package router
import (
"errors"
"fmt"
"github.com/go-playground/validator/v10"
"reflect"
"strings"
)
// GetValidateErr 格式化错误信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:19 2025/1/15
func GetValidateErr(obj any, rawErr error) error {
if nil == rawErr {
return nil
}
if nil == obj {
return rawErr
}
var (
ok bool
validationErrs validator.ValidationErrors
errString []string
field reflect.StructField
)
if ok = errors.As(rawErr, &validationErrs); !ok {
return rawErr
}
objType := reflect.TypeOf(obj)
if objType.Kind() == reflect.Ptr {
objType = objType.Elem()
}
for _, validationErr := range validationErrs {
if field, ok = objType.FieldByName(validationErr.Field()); ok {
if e := field.Tag.Get(defaultValidateErrTag); e != "" {
errString = append(errString, fmt.Sprintf("%s: %s", field.Tag.Get("json"), e))
continue
} else {
errString = append(errString, fmt.Sprintf("%s: %v", field.Tag.Get("json"), validationErr.Value()))
}
}
errString = append(errString, validationErr.Error())
}
return errors.New(strings.Join(errString, "\n"))
}