init: 通用库抽离——httpx/logger/wsc/wscclient/jwtx(合并分叉副本); 顶层包布局; module path git.zeroonesoft.cn/golib/zogo
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package httpx
|
||||
|
||||
import "fmt"
|
||||
|
||||
// CodeMsg 包含 code 与 msg 的错误结构,实现了 error 接口。
|
||||
type CodeMsg struct {
|
||||
Code int
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (c *CodeMsg) Error() string {
|
||||
return fmt.Sprintf("code: %d, msg: %s", c.Code, c.Msg)
|
||||
}
|
||||
|
||||
// New 创建一个 CodeMsg 错误。
|
||||
func New(code int, msg string) error {
|
||||
return &CodeMsg{Code: code, Msg: msg}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package httpx
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// Handle 泛型 handler 收口: Parse 取参 → 调 logic → 统一出参。
|
||||
// 试点新约定: 替代旧模板逐 handler 复制 Parse/OkJson/ErrorJson 的样板。
|
||||
func Handle[TReq any, TResp any](c *gin.Context, fn func(req *TReq) (*TResp, error)) {
|
||||
var req TReq
|
||||
if err := Parse(c, &req); err != nil {
|
||||
ErrorJson(c, err)
|
||||
return
|
||||
}
|
||||
resp, err := fn(&req)
|
||||
if err != nil {
|
||||
ErrorJson(c, err)
|
||||
return
|
||||
}
|
||||
OkJson(c, resp)
|
||||
}
|
||||
|
||||
// HandleResult 统一收口:err 非 nil 走 ErrorJson,否则 OkJson(data)。
|
||||
func HandleResult(c *gin.Context, data any, err error) {
|
||||
if err != nil {
|
||||
ErrorJson(c, err)
|
||||
return
|
||||
}
|
||||
OkJson(c, data)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Package httpx 提供 Gin 服务的统一出入参收口:统一 JSON 响应格式、
|
||||
// 错误码透出(CodeMsg)与请求参数绑定校验(Parse)。
|
||||
package httpx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// OkJson 以 200 OK 返回成功响应,统一格式:{code:0, msg:"OK", data:...}
|
||||
func OkJson(c *gin.Context, data any) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "OK",
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorJson 统一错误出参: CodeMsg 携带的错误码透出(如 401), 其余折叠为 code 1。
|
||||
func ErrorJson(c *gin.Context, err error) {
|
||||
code := 1
|
||||
msg := err.Error()
|
||||
var cm *CodeMsg
|
||||
if errors.As(err, &cm) {
|
||||
code = cm.Code
|
||||
msg = cm.Msg
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"msg": msg,
|
||||
"data": nil,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package httpx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// bindingValidator 与 gin 同源: 校验 struct 的 binding tag
|
||||
var bindingValidator = validator.New(validator.WithRequiredStructEnabled())
|
||||
|
||||
func init() {
|
||||
bindingValidator.SetTagName("binding")
|
||||
// 关闭 gin 内置校验 (ShouldBindQuery/Header/JSON 内部各自 validate, 会使 json 字段的
|
||||
// required 在 query 阶段误报)。校验统一由 Parse 末尾的 bindingValidator 收口。
|
||||
binding.Validator = nil
|
||||
}
|
||||
|
||||
// Parse 按统一顺序绑定请求参数: uri → query → header → json body。
|
||||
// 各来源只做绑定不做校验, 全部绑定完后统一跑 binding 校验。
|
||||
// (修复: gin 的 ShouldBindQuery 内部即执行校验, 会使 json 字段的 required 在 query 阶段误报)
|
||||
func Parse(c *gin.Context, v any) error {
|
||||
if err := parseUri(c, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseQuery(c, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseHeader(c, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseJSON(c, v); err != nil {
|
||||
return err
|
||||
}
|
||||
return validate(c, v)
|
||||
}
|
||||
|
||||
func parseUri(c *gin.Context, v any) error {
|
||||
if _, ok := c.Params.Get("id"); ok {
|
||||
return c.ShouldBindUri(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseQuery(c *gin.Context, v any) error {
|
||||
return c.ShouldBindQuery(v)
|
||||
}
|
||||
|
||||
func parseHeader(c *gin.Context, v any) error {
|
||||
return c.ShouldBindHeader(v)
|
||||
}
|
||||
|
||||
// parseJSON 直接解码 body, 不触发 gin 内部校验 (校验统一在 Parse 末尾)
|
||||
func parseJSON(c *gin.Context, v any) error {
|
||||
if c.Request == nil || c.Request.Body == nil || c.Request.ContentLength == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.NewDecoder(c.Request.Body).Decode(v)
|
||||
}
|
||||
|
||||
// Validator 请求结构体可实现此接口做自定义校验, 在 binding 校验之前调用
|
||||
type Validator interface {
|
||||
Validate() error
|
||||
}
|
||||
|
||||
func validate(c *gin.Context, v any) error {
|
||||
if vv, ok := v.(Validator); ok {
|
||||
if err := vv.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return bindingValidator.Struct(v)
|
||||
}
|
||||
Reference in New Issue
Block a user