// Package httpclient ... // // Description : httpclient ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2024-05-31 14:59 package httpclient import ( "fmt" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/network/httpclient/define" "git.zhangdeman.cn/zhangdeman/serialize" "git.zhangdeman.cn/zhangdeman/wrapper" "github.com/tidwall/gjson" "net/http" "net/textproto" "resty.dev/v3" "strings" ) // 初始化请求配置, 包括: // 1. 初始化空指针 // 2. 合并static参数 // 3. 使用 textproto.CanonicalMIMEHeaderKey 统一格式化header key func initRequestConfig(reqConfig *define.Request) { if nil == reqConfig.Header { reqConfig.Header = map[string]any{} } if nil == reqConfig.Body { reqConfig.Body = map[string]any{} } if nil == reqConfig.Cookie { reqConfig.Cookie = map[string]any{} } if reqConfig.Query == nil { reqConfig.Query = map[string]any{} } if reqConfig.PathParam == nil { reqConfig.PathParam = map[string]string{} } // 合并静态参数, 传入参数与静态参数存在相同参数, 以静态参数为准 for loc, paramTable := range reqConfig.Static { switch strings.ToUpper(loc) { case consts.RequestDataLocationBody.String(): for name, val := range paramTable { reqConfig.Body[name] = val } case consts.RequestDataLocationQuery.String(): for name, val := range paramTable { reqConfig.Query[name] = val } case consts.RequestDataLocationCookie.String(): for name, val := range paramTable { reqConfig.Cookie[name] = val } case consts.RequestDataLocationHeader.String(): for name, val := range paramTable { reqConfig.Header[name] = val } case consts.RequestDataLocationUriPath.String(): for name, val := range paramTable { reqConfig.PathParam[name] = fmt.Sprintf("%v", val) } } } formatHeader(reqConfig) } // NewRestyClient 获取resty client // // Author : go_developer@163.com<白茶清欢> // // Date : 15:00 2024/5/31 func NewRestyClient(reqConfig *define.Request) (*resty.Client, *resty.Request) { client := resty.New() request := client.R() if nil == reqConfig { return client, request } initRequestConfig(reqConfig) // 初始化 + 格式化配置 client.SetAllowMethodGetPayload(true) // 配置 GET 请求允许带 Body client.SetAllowMethodDeletePayload(true) // 配置 DELETE 请求允许带 Body client.SetJSONEscapeHTML(true) // 处理html实体字符 request.SetPathParams(reqConfig.PathParam) // 设置path中的参数 query := map[string]string{} for queryName, queryValue := range reqConfig.Query { if nil == queryValue { continue } query[queryName] = wrapper.AnyDataType(queryValue).ToString().Value() } request.SetQueryParams(query) // 设置query参数 header := map[string]string{} for headerName, headerValue := range reqConfig.Header { if nil == headerValue { continue } header[headerName] = wrapper.AnyDataType(headerValue).ToString().Value() } request.SetHeaders(header) // 设置header request.URL = reqConfig.FullUrl // 请求接口 // 设置url参数, url path格式需要是 /user/{user_id}/detail格式, {user_id} 会自动替换 request.SetPathParams(reqConfig.PathParam) request.Method = reqConfig.Method // 请求方法 cookieList := make([]*http.Cookie, 0) for cookieName, cookieValue := range reqConfig.Cookie { if nil == cookieValue { continue } cookieList = append(cookieList, &http.Cookie{ Name: cookieName, Value: wrapper.AnyDataType(cookieValue).ToString().Value(), }) } request.SetCookies(cookieList) // 设置cookie setRestyBody(reqConfig, request) // 设置请求Body return client, request } // setRestyBody 设置请求BODY TODO: 支持xml / yml等 // // Author : go_developer@163.com<白茶清欢> // // Date : 17:18 2024/5/31 func setRestyBody(reqConfig *define.Request, request *resty.Request) { if nil == reqConfig.Body || len(reqConfig.Body) == 0 { return } if strings.Contains(strings.ToLower(reqConfig.ContentType), consts.MimeTypeJson) { request.SetBody(reqConfig.Body) return } if strings.Contains(strings.ToLower(reqConfig.ContentType), consts.MimeTypeXWWWFormUrlencoded) { bodyStr := serialize.JSON.MarshalForStringIgnoreError(reqConfig.Body) bodyData := map[string]string{} jsonObj := gjson.Parse(bodyStr) jsonObj.ForEach(func(key, value gjson.Result) bool { bodyData[key.String()] = value.String() return true }) request.SetFormData(bodyData) } return } // formatHeader 格式化header // // Author : go_developer@163.com<白茶清欢> // // Date : 15:18 2024/5/31 func formatHeader(requestConfig *define.Request) { if nil == requestConfig { return } formatHeaderData := make(map[string]any) for headerName, headerVal := range requestConfig.Header { // 规范化处理 header 名称 formatHeaderData[textproto.CanonicalMIMEHeaderKey(headerName)] = headerVal } requestConfig.Header = formatHeaderData }