Files
common/http/http.go

172 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package http
import (
"context"
"fmt"
"net/http"
"reflect"
"regexp"
"strings"
_ "gitea.com/red-future/common/consul"
"gitea.com/red-future/common/jaeger"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/gsvc"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
var (
Httpserver = g.Server()
Httpclient = g.Client()
filterPaths = map[string]bool{"/": true, "/*": true, "/api.json": true}
legalRegisteredPaths = make(map[string]bool)
)
func init() {
err := gtime.SetTimeZone("Asia/Shanghai")
if err != nil {
panic("设置时区失败")
}
//s.Use(common.Cors) //中间件验证
//s.EnablePProf() //启用性能分析
//Httpserver.BindMiddlewareDefault(validateFilterPathMiddleware)
Httpserver.SetOpenApiPath("/api.json")
Httpserver.SetDumpRouterMap(true) //关闭打印路由注册信息
Httpserver.BindMiddlewareDefault(ghttp.MiddlewareHandlerResponse)
Httpclient.SetDiscovery(gsvc.GetRegistry())
}
func validateFilterPathMiddleware(r *ghttp.Request) {
path := r.URL.Path
if filterPaths[path] && !legalRegisteredPaths[path] {
routes := Httpserver.GetRoutes()
for _, route := range routes {
if filterPaths[route.Handler.Router.Uri] && route.Handler.Router.Uri != "/api.json" {
g.Log().Errorf(r.GetCtx(), "路径 %s 中间件 %s 必须通过 SkipMiddleware 注册", route.Handler.Router.Uri, route.Handler.Name)
}
}
r.ExitAll()
}
r.Middleware.Next()
}
func SkipMiddleware(h func(r *ghttp.Request), path string) (handler ghttp.HandlerFunc) {
if filterPaths[path] {
legalRegisteredPaths[path] = true
}
return func(r *ghttp.Request) {
if filterPaths[path] {
r.Middleware.Next()
return
}
h(r)
}
}
func RouteRegister(controllers []interface{}) {
//Httpserver.Group("/log", func(group *ghttp.RouterGroup) {
// group.Middleware(jaeger.NewTracer)
// group.Bind(controller.OperationLog)
//})
re := regexp.MustCompile("[A-Z]")
for _, t := range controllers {
sName := reflect.ValueOf(t).Elem().Type().Name()
convertedStr := re.ReplaceAllStringFunc(sName, func(s string) string {
return fmt.Sprintf("/%s", strings.ToLower(s))
})
Httpserver.Group(convertedStr, func(group *ghttp.RouterGroup) {
group.Middleware(jaeger.NewTracer)
group.Bind(t)
})
}
go Httpserver.Run()
}
// doRequest 统一HTTP请求处理DELETE用ContentJson发送bodygconv.Struct增加err检查
func doRequest(ctx context.Context, method string, url string, headers map[string]string, target any, data ...any) (err error) {
err = utils.ValidStructPtr(target)
if err != nil {
return
}
client := Httpclient.Clone()
// POST/PUT/DELETE请求都需要显式用ContentJson序列化body
if (method == http.MethodPost || method == http.MethodPut || method == http.MethodDelete) && len(data) > 0 {
client = client.ContentJson()
}
// 最后设置headers确保不会被ContentJson覆盖
if len(headers) > 0 {
client.SetHeaderMap(headers)
} else {
client.SetHeader("Authorization", g.RequestFromCtx(ctx).GetHeader("Authorization"))
}
// 修复避免data...展开导致的双重包装问题
// 当只有一个元素时,直接传递该元素,避免被包装成数组
var response *gclient.Response
// 对于GET请求将参数转换为map
if method == http.MethodGet && len(data) > 0 && len(data)%2 == 0 {
// 构建query参数map
queryParams := make(map[string]string)
for i := 0; i < len(data); i += 2 {
if key, ok := data[i].(string); ok && i+1 < len(data) {
queryParams[key] = gconv.String(data[i+1])
}
}
g.Log().Infof(ctx, "[HTTP] GET请求构建的query参数: %+v", queryParams)
response, err = client.DoRequest(ctx, method, url, queryParams)
} else if len(data) == 1 {
response, err = client.DoRequest(ctx, method, url, data[0])
} else {
response, err = client.DoRequest(ctx, method, url, data...)
}
if err != nil {
return
}
defer response.Close()
//result := response.ReadAll()
//
//// 统一处理内部API响应格式{code:200,message:"",data:{...}}
//resultStrut := &ghttp.DefaultHandlerResponse{}
//
//if err = gconv.Struct(result, &resultStrut); err != nil { // 修复增加err检查
// return errors.New("响应解析失败: " + err.Error())
//}
//
//// 添加调试日志:打印解析后的结构
//g.Log().Debugf(ctx, "[HTTP] 解析后结构: Code=%d, Message=%s, Data类型=%T, Data值=%+v",
// resultStrut.Code, resultStrut.Message, resultStrut.Data, resultStrut.Data)
//
//if resultStrut.Code == 200 || resultStrut.Code == 0 {
// if err = gconv.Struct(resultStrut.Data, target); err != nil { // 修复增加err检查
// return errors.New("数据解析失败: " + err.Error())
// }
// // 添加调试日志打印最终的target
// g.Log().Debugf(ctx, "[HTTP] 最终target: %+v", target)
//} else {
// err = errors.New(resultStrut.Message)
//}
return
}
func Get(ctx context.Context, url string, headers map[string]string, target any, data ...any) (err error) {
err = doRequest(ctx, http.MethodGet, url, headers, target, data...)
return
}
func Post(ctx context.Context, url string, headers map[string]string, target any, data ...any) (err error) {
err = doRequest(ctx, http.MethodPost, url, headers, target, data...)
return
}
func Put(ctx context.Context, url string, headers map[string]string, target any, data ...any) (err error) {
err = doRequest(ctx, http.MethodPut, url, headers, target, data...)
return
}
func Delete(ctx context.Context, url string, headers map[string]string, target any, data ...any) (err error) {
err = doRequest(ctx, http.MethodDelete, url, headers, target, data...)
return
}