Compare commits

...

13 Commits

11 changed files with 434 additions and 301 deletions

27
go.mod
View File

@ -7,32 +7,29 @@ toolchain go1.21.5
require (
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20231105153815-e8561a060cc8
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240627031706-9ff1c213bb50
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b
github.com/gin-gonic/gin v1.10.0
github.com/go-playground/validator/v10 v10.22.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
)
require (
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda // indirect
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 // indirect
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a // 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-20240723075210-85feada512b2 // indirect
github.com/BurntSushi/toml v1.4.0 // 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/bytedance/sonic v1.12.1 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
@ -51,19 +48,19 @@ require (
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/pmezard/go-difflib v1.0.0 // 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/tidwall/gjson v1.17.3 // 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
golang.org/x/arch v0.9.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

34
go.sum
View File

@ -1,7 +1,3 @@
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/consts v0.0.0-20240723085016-ee0510753552 h1:SgxcaLXEXn3ImMOik9y3xDz82KwK/+9IhqV1kZbqIwQ=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240723085016-ee0510753552/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4 h1:mibnyzYbZullK0aTHVASHl3UeoVr8IgytQZsuyv+yEM=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=
@ -24,14 +20,22 @@ git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2 h1:P2k
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2/go.mod h1:7KaMpQmWgiNLpEkaV7oDtep7geI1f/VoCoPVcyvMjAE=
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/wrapper v0.0.0-20240806072320-3533617196fd h1:zcmfmp/1srHldFpa7BCH6Cpp9iVNFM/7W7DoxHp48UU=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b h1:CcO2t7ssBSZwE7BDTOkCSgOvTGATarOZ0tajZ00McEc=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo=
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/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 v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/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=
@ -40,11 +44,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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 v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
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/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
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/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
@ -61,11 +66,13 @@ github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4
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/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.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/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=
@ -74,6 +81,7 @@ github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
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=
@ -114,6 +122,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
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/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
github.com/tidwall/gjson v1.17.3/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=
@ -132,16 +142,26 @@ 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/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
golang.org/x/arch v0.9.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/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
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/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
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/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.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/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
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=

View File

@ -11,7 +11,6 @@ import (
"bytes"
"errors"
"io"
"io/ioutil"
"net/http"
"strings"
@ -42,7 +41,7 @@ func (f *form) Parse(ctx *gin.Context, receiver interface{}) error {
// 请求信息写入上下文
ctx.Set(handleConfig.RecordRequestDataField, string(requestBody))
// 因为请求体被读一遍之后就没了,重新赋值 requestBody
ctx.Request.Body = ioutil.NopCloser(bytes.NewReader(requestBody))
ctx.Request.Body = io.NopCloser(bytes.NewReader(requestBody))
method := strings.ToUpper(ctx.Request.Method)
if method == http.MethodGet ||
method == http.MethodPatch ||

110
router/README.md Normal file
View File

@ -0,0 +1,110 @@
# 路由包使用说明
## 基础说明
- 基于 [web框架GIN](https://github.com/gin-gonic/gin) 的二次封装
## 为什么要二次封装
二次封装的想法来源于 [web框架GoFrame](https://github.com/gogf/gf)。gf框架本身内置集成了大量组件, 对于轻量应用开发, 相比较而言比较笨重, 但是启根据请求数据结构、响应数据结构自动生成表单的能力十分实用。
在采用gin开发时一般接口文档有两种选择 :
- 在内部文档平台手搓文档, 并人工维护, 此方案最终演变方式大概率为文档主键滞后,与接口实现并不一致
- 利用golang的文档生成工具[swag]() 自动生成swagger文档, 此种方案需要在代码中引入大量swag工具解析时需要的注释, 且当输入/输出发生变化时需要维护相关注释
基于以上问题, 对gin的路由注册进行二次封装
## 二次封装解决哪些问题
- 文档维护的繁琐性 : 会通过反射自动基于 **`请求的数据结构 + 返回的数据结构`** , 生成接口文档, 专注于数据结构设计即可, 无需关注文档相关内容, 程序接管, 自动生成
- 做接口设计的统一性规范约束, 如 :
- 入参数据类型不能为 any
- 入参类型不能为 map , 但是特定字段特定场景, 可以强行禁用此规则等
- 返回值必须为结构体等
## 设计方案
### 【可选】路由组定义
路由组可以通过函数进行定义, 函数名必须需为 : **`RouterPrefix`** , 函数无任何参数, 返回路由组名称, 示例 :
```go
type TestController struct{}
func (t *TestController) RouterPrefix() string {
return "/uri/prefix"
}
```
### 【可选】路由组中间件定义
路由组中间件可以通过函数进行定义, 函数名必须需为 : **`RouterMiddleware`** , 函数无任何参数, 返回路由组中间件列表 **`[]gin.HandlerFunc`**, 示例 :
```go
type TestController struct{}
func (t *TestController) RouterPrefix() string {
return "/uri/prefix"
}
func (t *TestController) RouterMiddleware() []gin.HandlerFunc {
return []gin.HandlerFunc{
func(ctx *gin.Context) {
},
}
}
```
### 接口逻辑处理定义
接口逻辑处理函数, 函数名称自定义, 必须是个 **`可导出函数`** , 函数接收两个参数, 分别是 :
- *gin.Context : gin框架的上下文信息
- any: 表单参数 **结构体指针** , 注意 : 类型必须为结构体指针
函数返回值有两个 :
- any : 返回的业务数据, 必须是个 **`结构体指针`**
- error : 可以是内置的 **error** , 也可以是 **exception.IException** , 建议使用 exception.IException , 可承载更多的异常信息
## 使用方式
```go
type TestController struct{}
func (t *TestController) RouterPrefix() string {
return "/uri/prefix"
}
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 TestForm struct {
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
Age int `json:"age" form:"age"`
Name string `json:"name" form:"name"`
Test *Test `json:"test" form:"test"`
Num *int64 `json:"num" form:"num"`
}
type Test struct {
L string `json:"l"`
}
func Test_parseController(t *testing.T) {
type args struct {
controller any
}
Register(8080, &TestController{})
}
```
注意事项:
- Register 方法第一个入参是监听的端口
- 后面若干个controller实例, 朱一必须以指针的方式传入

View File

@ -1,35 +0,0 @@
// Package router ...
//
// Description : 便捷的相关API处理
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-03-26 2:06 下午
package router
import (
"github.com/gin-gonic/gin"
)
// IApi 每一个接口的实现约束
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:08 下午 2021/3/26
type IApi interface {
// GetMethod 接口请求方法
GetMethod() string
// GetURI 接口URI
GetURI() string
// GetMiddleWareList 使用的中间件列表
GetMiddleWareList() []gin.HandlerFunc
// GetHandler 处理的handler
GetHandler() gin.HandlerFunc
}
// RegisterFunc 注册路由的函数
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:09 下午 2021/3/26
type RegisterFunc func() (method string, uri string, handlerFunc gin.HandlerFunc, middlewareList []gin.HandlerFunc)

37
router/define.go Normal file
View File

@ -0,0 +1,37 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-07-20 22:57
package router
import "reflect"
const (
PrefixFuncName = "RouterPrefix" // 路由前缀函数名称
MiddlewareFuncName = "RouterMiddleware" // 路由中间件函数名称
)
const (
TagNamePath = "path" // 接口的请求路径
TagNameMethod = "method" // 接口的请求方法
TagNameUriTag = "tag" // 接口的tag
TagNameDesc = "desc" // 接口的描述
TagNameStrict = "strict" // 接口是否为严格模式 : 不配置, 则为严格模式.严格模式 : POST 仅解析 BODY , GET 仅解析 QUERY
)
// UriConfig 接口配置
//
// Author : go_developer@163.com<白茶清欢>
//
// 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:"-"` // 表单数据类型
}

15
router/meta.go Normal file
View File

@ -0,0 +1,15 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-07-20 21:40
package router
// Meta 接口的元信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:40 2024/7/20
type Meta struct{}

View File

@ -1,115 +1,213 @@
// Package router ...
//
// Description : 注册路由
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-03-26 2:13 下午
// Date : 2024-07-20 21:39
package router
import (
"log"
"reflect"
"github.com/pkg/errors"
"fmt"
"git.zhangdeman.cn/zhangdeman/exception"
"git.zhangdeman.cn/zhangdeman/gin/middleware"
"git.zhangdeman.cn/zhangdeman/gin/request"
"git.zhangdeman.cn/zhangdeman/gin/response"
"git.zhangdeman.cn/zhangdeman/wrapper"
"github.com/gin-gonic/gin"
"net/http"
"reflect"
"strings"
)
var (
// DebugLogEnable 默认打开debug日志
DebugLogEnable = true
Debug = false // 是否开启DEBUG
ginRouter = gin.Default()
)
// DisableDebugLog 禁用debug日志
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:17 下午 2021/3/26
func DisableDebugLog() {
DebugLogEnable = false
func init() {
}
// RegisterRouter 注册一个路由
// Register 注册路由
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:14 下午 2021/3/26
func RegisterRouter(router *gin.Engine, apiInstanceList ...any) error {
for _, apiInstance := range apiInstanceList {
if nil == apiInstance {
// Date : 21:40 2024/7/20
func Register(port int, controllerList ...any) error {
for _, controller := range controllerList {
if nil == controller {
// 忽略空指针
continue
}
val := reflect.ValueOf(apiInstance)
switch val.Kind() {
case reflect.Struct:
fallthrough
case reflect.Ptr:
api, ok := apiInstance.(IApi)
if ok {
if err := HandleRegisterRouter(router, api.GetMethod(), api.GetURI(), api.GetHandler(), api.GetMiddleWareList()); nil != err {
routerLog(err.Error())
return err
parseController(controller)
}
continue
}
routerLog(val.String() + "结构体或者结构体指针, 自动识别函数是否包含RouterFunc")
// 不是IApi接口,自动识别函数列表 RouterFunc 函数自动注册
methodCnt := val.NumMethod()
for i := 0; i < methodCnt; i++ {
// TODO : 识别函数本身是不是 RouterFunc
af, o := val.Method(i).Interface().(func() (string, string, gin.HandlerFunc, []gin.HandlerFunc))
if o {
method, uri, handler, middlewareList := af()
if err := HandleRegisterRouter(router, method, uri, handler, middlewareList); nil != err {
routerLog(err.Error())
return err
}
continue
}
apiFuncList := val.Method(i).Call(nil)
for _, apiFuncVal := range apiFuncList {
apiFunc, ok := apiFuncVal.Interface().(RegisterFunc)
if !ok {
continue
}
method, uri, handler, middlewareList := apiFunc()
if err := HandleRegisterRouter(router, method, uri, handler, middlewareList); nil != err {
routerLog(err.Error())
return err
}
}
}
case reflect.Func:
api, ok := apiInstance.(RegisterFunc)
if !ok {
err := errors.New("函数方式注册路由必须是 RouterFunc")
routerLog(err.Error())
return err
}
method, uri, handler, middlewareList := api()
if err := HandleRegisterRouter(router, method, uri, handler, middlewareList); nil != err {
routerLog(err.Error())
return err
}
default:
err := errors.New("注册的路由必须是 IApi 或者 RouterFunc 或者 包含 RouterFunc 的结构体")
routerLog(err.Error())
return err
}
}
return nil
return ginRouter.Run(fmt.Sprintf(":%d", port))
}
// routerLog 记录日志
// parseController 解析controller
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:28 下午 2021/3/26
func routerLog(msg string) {
if !DebugLogEnable || len(msg) == 0 {
// Date : 22:10 2024/7/20
func parseController(controller any) {
controllerType := reflect.TypeOf(controller)
controllerValue := reflect.ValueOf(controller)
routerPrefix := "/"
// 解析路由前缀函数
// routerPrefix 不能有任何入参, 并且只能有一个返回值,
// 返回值类型为字符串, 为具体路由前缀
routerPrefixFunc, routerPrefixFuncExist := controllerType.MethodByName(PrefixFuncName)
if routerPrefixFuncExist {
routerPrefixFuncType := routerPrefixFunc.Type
if routerPrefixFuncType.NumIn() == 1 && // 无任何入参, 正在没有如何入参情况下, 第一个参数是结构体指针
routerPrefixFuncType.NumOut() == 1 && // 只能有一个返回值
routerPrefixFuncType.Out(0).Kind() == reflect.String { // 返回值必须是字符串
routerPrefix = routerPrefixFunc.Func.Call([]reflect.Value{controllerValue})[0].String()
}
}
// 请求组的中间件
middlewareList := make([]gin.HandlerFunc, 0)
routerMiddlewareFunc, routerMiddlewareFuncExist := controllerType.MethodByName(MiddlewareFuncName)
if routerMiddlewareFuncExist {
routerMiddlewareFuncType := routerMiddlewareFunc.Type
if routerMiddlewareFuncType.NumIn() == 1 && // 无需任何参数
routerMiddlewareFuncType.NumOut() == 1 && // 只能有一个返回值
routerMiddlewareFuncType.Out(0).String() == "[]gin.HandlerFunc" { // 返回值必须是gin.HandlerFunc
res := routerMiddlewareFunc.Func.Call([]reflect.Value{controllerValue})
if !res[0].IsNil() {
middlewareList = res[0].Interface().([]gin.HandlerFunc)
}
}
}
for funcIdx := 0; funcIdx < controllerType.NumMethod(); funcIdx++ {
method := controllerType.Method(funcIdx)
if method.Name == PrefixFuncName || method.Name == MiddlewareFuncName {
continue
}
methodType := method.Type
uriConfig, err := parseUriConfig(methodType, routerPrefix)
if nil != err {
debugLog("parseUriConfig error : %s -> %s", err.Error(), methodType.Kind().String())
}
if nil == uriConfig {
continue
}
registerUri(uriConfig, controllerValue.Method(funcIdx), middlewareList)
}
}
// parseUriConfig 解析Uri配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:40 2024/7/21
func parseUriConfig(methodType reflect.Type, routerPrefix string) (*UriConfig, error) {
if methodType.NumIn() != 3 || // 结构体指针 + 两个参数
methodType.NumOut() != 2 { // 两个返回值
return nil, nil
}
// 接口logic共计两个参数. 两个返回值, 格式 : func(ctx *gin.Context, formData any[组合Meta]) (any[response], error)
// 解析第一个参数是 *gin.Context
if methodType.In(1).String() != "*gin.Context" {
return nil, nil
}
// 解析第二个参数是组合Meta的form表单
formType := methodType.In(2)
if formType.Kind() == reflect.Ptr {
formType = methodType.In(2).Elem()
}
metaField, metaFieldExist := formType.FieldByName("Meta")
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(),
}
// 校验 FormDataType
for fieldIdx := 0; fieldIdx < uriConfig.FormDataType.NumField(); fieldIdx++ {
if uriConfig.FormDataType.Field(fieldIdx).Type.Kind() == reflect.Interface {
panic("request param set type `interface` is not allowed")
}
}
return uriConfig, nil
}
// registerUri 注册路由
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:00 2024/7/21
func registerUri(uriConfig *UriConfig, methodValue reflect.Value, middlewareList []gin.HandlerFunc) {
if nil == middlewareList {
middlewareList = make([]gin.HandlerFunc, 0)
}
handlerFunc := func(ctx *gin.Context) {
formDataReceiver := reflect.New(uriConfig.FormDataType)
if err := request.Form.Parse(ctx, formDataReceiver.Interface()); nil != err {
panic(err)
}
returnValue := methodValue.Call([]reflect.Value{reflect.ValueOf(ctx), formDataReceiver})
businessData := returnValue[0].Interface()
errData := returnValue[1]
if errData.IsNil() {
response.Success(ctx, businessData)
return
}
log.Print(msg)
err := errData.Interface()
if e, ok := err.(exception.IException); ok {
response.SendWithException(ctx, e, businessData)
return
} else {
response.SendWithException(ctx, exception.NewFromError(-1, errData.Interface().(error)), businessData)
return
}
}
middlewareList = append(middlewareList, handlerFunc)
middlewareList = append([]gin.HandlerFunc{
middleware.InitRequest(),
}, middlewareList...)
switch uriConfig.Method {
case http.MethodGet:
ginRouter.GET(uriConfig.Path, middlewareList...)
case http.MethodHead:
ginRouter.HEAD(uriConfig.Path, middlewareList...)
case http.MethodPost:
ginRouter.PUT(uriConfig.Path, middlewareList...)
case http.MethodPut:
ginRouter.PUT(uriConfig.Path, middlewareList...)
case http.MethodPatch:
ginRouter.PATCH(uriConfig.Path, middlewareList...)
case http.MethodDelete:
ginRouter.DELETE(uriConfig.Path, middlewareList...)
case http.MethodConnect:
ginRouter.Handle(http.MethodConnect, uriConfig.Path, middlewareList...)
case http.MethodOptions:
ginRouter.OPTIONS(uriConfig.Path, middlewareList...)
case http.MethodTrace:
ginRouter.Handle(http.MethodTrace, uriConfig.Path, middlewareList...)
case "ANY":
ginRouter.Any(uriConfig.Path, middlewareList...)
default:
panic(uriConfig.Path + " : " + uriConfig.Method + " is not support")
}
}
// debugLog ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:32 2024/7/21
func debugLog(format string, valList ...any) {
if !Debug {
return
}
fmt.Printf("[DEBUG] "+format+"\n", valList...)
}

View File

@ -1,78 +0,0 @@
// Package router ...
//
// Description : 路由注册单元测试
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-03-26 3:49 下午
package router
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/gin-gonic/gin"
)
// TestRegisterRouter ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:50 下午 2021/3/26
func TestRegisterRouter(t *testing.T) {
r := gin.Default()
err := RegisterRouter(r, demoApiFunc(), &demoApi{}, &otherApi{}, nil)
assert.Nil(t, err, "路由注册异常 : %v", err)
}
func demoApiFunc() RouterFunc {
return func() (method string, uri string, handlerFunc gin.HandlerFunc, middlewareList []gin.HandlerFunc) {
return http.MethodGet, "/api/func/test", func(context *gin.Context) {
}, nil
}
}
type demoApi struct {
}
func (d demoApi) GetMethod() string {
return http.MethodGet
}
func (d demoApi) GetURI() string {
return "/api/struct/test"
}
func (d demoApi) GetMiddleWareList() []gin.HandlerFunc {
return nil
}
func (d demoApi) GetHandler() gin.HandlerFunc {
return func(context *gin.Context) {
}
}
type otherApi struct {
}
func (oa *otherApi) DemoApiFunc() RouterFunc {
return func() (method string, uri string, handlerFunc gin.HandlerFunc, middlewareList []gin.HandlerFunc) {
return http.MethodGet, "/api/other/test", func(context *gin.Context) {
}, nil
}
}
func (oa *otherApi) Lala() {
}
func (oa *otherApi) SelfApi() (method string, uri string, handlerFunc gin.HandlerFunc, middlewareList []gin.HandlerFunc) {
return http.MethodGet, "/api/other/self/test", func(context *gin.Context) {
}, nil
}

49
router/register_test.go Normal file
View File

@ -0,0 +1,49 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-07-20 23:24
package router
import (
"testing"
"github.com/gin-gonic/gin"
)
type TestController struct{}
func (t *TestController) RouterPrefix() string {
return "/uri/prefix"
}
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 TestForm struct {
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
Age int `json:"age" form:"age"`
Name string `json:"name" form:"name"`
Test *Test `json:"test" form:"test"`
Num *int64 `json:"num" form:"num"`
}
type Test struct {
L string `json:"l"`
}
func Test_parseController(t *testing.T) {
type args struct {
controller any
}
Register(8080, &TestController{})
}

View File

@ -1,79 +0,0 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2023-03-03 16:48
package router
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// HandleRegisterRouter 注册gin路由
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:36 下午 2021/3/9
func HandleRegisterRouter(router *gin.Engine, method string, uri string, handler gin.HandlerFunc, middlewareList []gin.HandlerFunc) error {
if nil == middlewareList {
middlewareList = make([]gin.HandlerFunc, 0)
}
switch strings.ToUpper(method) {
case http.MethodGet:
router.GET(uri, handler).Use(middlewareList...)
case http.MethodPost:
router.POST(uri, handler).Use(middlewareList...)
case http.MethodDelete:
router.DELETE(uri, handler).Use(middlewareList...)
case http.MethodHead:
router.HEAD(uri, handler).Use(middlewareList...)
case http.MethodOptions:
router.OPTIONS(uri, handler).Use(middlewareList...)
case http.MethodPatch:
router.PATCH(uri, handler).Use(middlewareList...)
case http.MethodPut:
router.PUT(uri, handler).Use(middlewareList...)
case "ANY": // 一次性注册全部请求方法的路由
router.Any(uri, handler).Use(middlewareList...)
default:
// 不是一个函数,数名method配置错误
return fmt.Errorf("uri=%s method=%s 请求方法配置错误", uri, method)
}
return nil
}
// RegisterRouterGroup 注册gin路由
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:36 下午 2021/3/9
func RegisterRouterGroup(router *gin.RouterGroup, method string, uri string, handler gin.HandlerFunc) error {
switch strings.ToUpper(method) {
case http.MethodGet:
router.GET(uri, handler)
case http.MethodPost:
router.POST(uri, handler)
case http.MethodDelete:
router.DELETE(uri, handler)
case http.MethodHead:
router.HEAD(uri, handler)
case http.MethodOptions:
router.OPTIONS(uri, handler)
case http.MethodPatch:
router.PATCH(uri, handler)
case http.MethodPut:
router.PUT(uri, handler)
case "ANY": // 一次性注册全部请求方法的路由
router.Any(uri, handler)
default:
// 不是一个函数,数名method配置错误
return fmt.Errorf("uri=%s method=%s 请求方法配置错误", uri, method)
}
return nil
}