// Package httpclient ... // // Description : httpclient ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2024-05-31 14:59 package httpclient import ( "errors" "fmt" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/network/httpclient/define" requestBoodyWriter "git.zhangdeman.cn/zhangdeman/network/httpclient/implement/request" "git.zhangdeman.cn/zhangdeman/wrapper" "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, reqOption *RequestOption) (*resty.Client, *resty.Request, error) { client := resty.New() request := client.R() if nil == reqConfig { return nil, nil, errors.New("request config is nil") } // 限流处理, 增加限流中间件 client.AddRequestMiddleware(func(client *resty.Client, request *resty.Request) error { if nil != reqOption && nil != reqOption.RateLimiter { // 未配置流控 return nil } if !reqOption.RateLimiter.Allow() { // 命中流控 return define.ErrRateLimitExceeded } return nil }) 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 if err := setRestyBody(reqConfig, reqOption, request); nil != err { return nil, nil, err } return client, request, nil } // setRestyBody 设置请求BODY // // Author : go_developer@163.com<白茶清欢> // // Date : 17:18 2024/5/31 func setRestyBody(reqConfig *define.Request, requestOption *RequestOption, request *resty.Request) error { if nil == reqConfig.Body { return nil } if nil != requestOption && nil != requestOption.RequestBodyWrite { // 外部传入的实现 requestOption.RequestBodyWrite.Write(request, reqConfig.Body) return nil } // 外部没传入, 使用内置实现, 内置默认实现支持: xml/json/form, 如需其他, 自行扩展 return requestBoodyWriter.WriteBody(reqConfig.ContentType, request, reqConfig.Body) } // 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 }