From 1403693fda8c801734a4437c60c9fb7103943f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 8 May 2025 15:12:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=87=AA=E9=80=82=E5=BA=94?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E7=B1=BB=E5=9E=8B,=20=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E8=AF=B7=E6=B1=82=E7=B1=BB=E5=9E=8B=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=AD=A3=E7=A1=AEBody=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- httpclient/abstract/IRequestBodyWrite.go | 16 +++++++ httpclient/client.go | 8 +++- httpclient/implement/request/write_body.go | 55 ++++++++++++++++++++++ httpclient/implement/request/write_form.go | 33 +++++++++++++ httpclient/implement/request/write_json.go | 21 +++++++++ httpclient/implement/request/write_xml.go | 46 ++++++++++++++++++ httpclient/mesh/define.go | 1 + httpclient/option.go | 7 +-- httpclient/resty.go | 46 ++++++++---------- 9 files changed, 203 insertions(+), 30 deletions(-) create mode 100644 httpclient/abstract/IRequestBodyWrite.go create mode 100644 httpclient/implement/request/write_body.go create mode 100644 httpclient/implement/request/write_form.go create mode 100644 httpclient/implement/request/write_json.go create mode 100644 httpclient/implement/request/write_xml.go diff --git a/httpclient/abstract/IRequestBodyWrite.go b/httpclient/abstract/IRequestBodyWrite.go new file mode 100644 index 0000000..ec49eb4 --- /dev/null +++ b/httpclient/abstract/IRequestBodyWrite.go @@ -0,0 +1,16 @@ +// Package abstract ... +// +// Description : abstract ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2025-05-08 14:18 +package abstract + +import "resty.dev/v3" + +// IRequestBodyWrite 请求信息写入Body的接口约束 +type IRequestBodyWrite interface { + // Write 写入请求Body + Write(request *resty.Request, bodyData map[string]any) error +} diff --git a/httpclient/client.go b/httpclient/client.go index 273e969..2354554 100644 --- a/httpclient/client.go +++ b/httpclient/client.go @@ -99,7 +99,13 @@ func NewHttpClient(reqConfig *define.Request, reqOption *RequestOption) (*HttpCl if ua, exist := reqConfig.Header[consts.HeaderKeyUserAgent.String()]; !exist || nil == ua || fmt.Sprintf("%v", ua) == "" { reqConfig.Header[consts.HeaderKeyUserAgent.String()] = "resty-v3@network/httpclient" } - restyClient, restyRequest := NewRestyClient(reqConfig, reqOption) + restyClient, restyRequest, err := NewRestyClient(reqConfig, reqOption) + if nil != err { + if nil != restyClient { + _ = restyClient.Close() + } + return nil, err + } defer restyClient.Close() hc := &HttpClient{ diff --git a/httpclient/implement/request/write_body.go b/httpclient/implement/request/write_body.go new file mode 100644 index 0000000..edf4d2d --- /dev/null +++ b/httpclient/implement/request/write_body.go @@ -0,0 +1,55 @@ +// Package request ... +// +// Description : request ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2025-05-08 14:21 +package request + +import ( + "errors" + "git.zhangdeman.cn/zhangdeman/network/httpclient/abstract" + "resty.dev/v3" + "strings" +) + +var ( + // WriteBodyInstanceTable 请求类型 => 请求类型写入的实现 + WriteBodyInstanceTable = map[string]abstract.IRequestBodyWrite{ + "json": &WriteJson{}, + "xml": nil, + "x-www-form-urlencoded": &WriteForm{}, + } +) + +func SetWriteBodyInstance(cType string, instance abstract.IRequestBodyWrite) { + WriteBodyInstanceTable[cType] = instance +} + +func DeleteWriteBodyInstance(cType string) { + delete(WriteBodyInstanceTable, cType) +} + +// WriteBody 数据写入body +func WriteBody(contentType string, request *resty.Request, bodyData map[string]any) error { + if nil == bodyData { + // body为nil, 无需写入, 否则即使为空map也要正常写入 + return nil + } + if nil == request { + return errors.New("request is nil") + } + contentType = strings.TrimSpace(strings.Split(contentType, ";")[0]) + if len(contentType) == 0 { + return errors.New("contentType is empty or invalid") + } + contentTypeArr := strings.Split(contentType, "/") + realType := contentTypeArr[len(contentTypeArr)-1] + writeInstance, ok := WriteBodyInstanceTable[realType] + if !ok || writeInstance == nil { + return errors.New(realType + " is not support, writeInstance is nil") + } + writeInstance.Write(request, bodyData) + return nil +} diff --git a/httpclient/implement/request/write_form.go b/httpclient/implement/request/write_form.go new file mode 100644 index 0000000..0794c08 --- /dev/null +++ b/httpclient/implement/request/write_form.go @@ -0,0 +1,33 @@ +// Package request ... +// +// Description : request ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2025-05-08 14:39 +package request + +import ( + "git.zhangdeman.cn/zhangdeman/serialize" + "github.com/tidwall/gjson" + "resty.dev/v3" +) + +// WriteForm application/x-www-form-urlencoded 实现 +type WriteForm struct { +} + +func (w *WriteForm) Write(request *resty.Request, bodyData map[string]any) error { + if len(bodyData) == 0 { + return nil + } + bodyStr := serialize.JSON.MarshalForStringIgnoreError(bodyData) + formatBodyData := map[string]string{} + jsonObj := gjson.Parse(bodyStr) + jsonObj.ForEach(func(key, value gjson.Result) bool { + formatBodyData[key.String()] = value.String() + return true + }) + request.SetFormData(formatBodyData) + return nil +} diff --git a/httpclient/implement/request/write_json.go b/httpclient/implement/request/write_json.go new file mode 100644 index 0000000..0518070 --- /dev/null +++ b/httpclient/implement/request/write_json.go @@ -0,0 +1,21 @@ +// Package request ... +// +// Description : request ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2025-05-08 14:21 +package request + +import ( + "resty.dev/v3" +) + +// WriteJson application/json写入 +type WriteJson struct { +} + +func (w *WriteJson) Write(request *resty.Request, bodyData map[string]any) error { + request.SetBody(bodyData) + return nil +} diff --git a/httpclient/implement/request/write_xml.go b/httpclient/implement/request/write_xml.go new file mode 100644 index 0000000..4f70611 --- /dev/null +++ b/httpclient/implement/request/write_xml.go @@ -0,0 +1,46 @@ +// Package request ... +// +// Description : request ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2025-05-08 14:21 +package request + +import ( + "bytes" + "errors" + "git.zhangdeman.cn/zhangdeman/serialize" + "resty.dev/v3" + "git.zhangdeman.cn/zhangdeman/dynamic-struct/wrapper" +) + +// WriteXml application/xml body写入 +type WriteXml struct{} + +func (w *WriteXml) Write(request *resty.Request, bodyData map[string]any) error { + if nil == bodyData { + return nil + } + var ( + err error + bodyBytes []byte + xmlBodyByte []byte + ) + + if bodyBytes, err = serialize.JSON.MarshalForByte(bodyData); nil != err { + return err + } + instance, err := wrapper.NewJson(string(bodyBytes), &wrapper.Option{XmlName: "RequestBody"}) + if nil != err { + return err + } + if nil == instance { + return errors.New("xml generate instance is nil") + } + if xmlBodyByte, err = instance.Marshal("xml"); nil != err { + return err + } + request.SetBody(bytes.NewReader(xmlBodyByte)) + return nil +} diff --git a/httpclient/mesh/define.go b/httpclient/mesh/define.go index 3878dce..3a1bc57 100644 --- a/httpclient/mesh/define.go +++ b/httpclient/mesh/define.go @@ -41,6 +41,7 @@ type RequestConfigGroupItem struct { CacheInstance abstract.ICache `json:"-"` // 数据缓存实例 RateLimiter abstract.RateLimiter `json:"-"` // 流控实例 ResponseParser abstract.IResponse `json:"-"` // 响应数据解析 + RequestBodyWrite abstract.IRequestBodyWrite `json:"-"` // 请求Body数据写入的实现 Condition any `json:"condition"` // TODO: 请求条件, 特定条件下不执行当前请求 } diff --git a/httpclient/option.go b/httpclient/option.go index 3c4e960..5cdb2dd 100644 --- a/httpclient/option.go +++ b/httpclient/option.go @@ -13,7 +13,8 @@ import ( // RequestOption 请求一些选项 type RequestOption struct { - CacheInstance abstract.ICache `json:"-"` // 数据结果缓存实例 - RateLimiter abstract.RateLimiter `json:"-"` // 流控实例 - ResponseParser abstract.IResponse `json:"-"` // 返回结果解析, 不配置使用内置实现 + CacheInstance abstract.ICache `json:"-"` // 数据结果缓存实例 + RateLimiter abstract.RateLimiter `json:"-"` // 流控实例 + RequestBodyWrite abstract.IRequestBodyWrite `json:"-"` // 将请求Body写入请求实例 + ResponseParser abstract.IResponse `json:"-"` // 返回结果解析, 不配置使用内置实现 } diff --git a/httpclient/resty.go b/httpclient/resty.go index 1cd9757..e4c51ba 100644 --- a/httpclient/resty.go +++ b/httpclient/resty.go @@ -8,12 +8,12 @@ package httpclient import ( + "errors" "fmt" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/network/httpclient/define" - "git.zhangdeman.cn/zhangdeman/serialize" + requestBoodyWriter "git.zhangdeman.cn/zhangdeman/network/httpclient/implement/request" "git.zhangdeman.cn/zhangdeman/wrapper" - "github.com/tidwall/gjson" "net/http" "net/textproto" "resty.dev/v3" @@ -75,11 +75,11 @@ func initRequestConfig(reqConfig *define.Request) { // Author : go_developer@163.com<白茶清欢> // // Date : 15:00 2024/5/31 -func NewRestyClient(reqConfig *define.Request, reqOption *RequestOption) (*resty.Client, *resty.Request) { +func NewRestyClient(reqConfig *define.Request, reqOption *RequestOption) (*resty.Client, *resty.Request, error) { client := resty.New() request := client.R() if nil == reqConfig { - return client, request + return nil, nil, errors.New("request config is nil") } // 限流处理, 增加限流中间件 client.AddRequestMiddleware(func(client *resty.Client, request *resty.Request) error { @@ -93,7 +93,7 @@ func NewRestyClient(reqConfig *define.Request, reqOption *RequestOption) (*resty } return nil }) - + initRequestConfig(reqConfig) // 初始化 + 格式化配置 client.SetAllowMethodGetPayload(true) // 配置 GET 请求允许带 Body client.SetAllowMethodDeletePayload(true) // 配置 DELETE 请求允许带 Body @@ -130,35 +130,29 @@ func NewRestyClient(reqConfig *define.Request, reqOption *RequestOption) (*resty Value: wrapper.AnyDataType(cookieValue).ToString().Value(), }) } - request.SetCookies(cookieList) // 设置cookie - setRestyBody(reqConfig, request) // 设置请求Body - return client, request + request.SetCookies(cookieList) // 设置cookie + if err := setRestyBody(reqConfig, reqOption, request); nil != err { + return nil, nil, err + } + return client, request, nil } -// setRestyBody 设置请求BODY TODO: 支持xml / yml等 +// setRestyBody 设置请求BODY // // 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 +func setRestyBody(reqConfig *define.Request, requestOption *RequestOption, request *resty.Request) error { + if nil == reqConfig.Body { + return nil } - if strings.Contains(strings.ToLower(reqConfig.ContentType), consts.MimeTypeJson) { - request.SetBody(reqConfig.Body) - return + if nil != requestOption && nil != requestOption.RequestBodyWrite { + // 外部传入的实现 + requestOption.RequestBodyWrite.Write(request, reqConfig.Body) + return nil } - 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 + // 外部没传入, 使用内置实现, 内置默认实现支持: xml/json/form, 如需其他, 自行扩展 + return requestBoodyWriter.WriteBody(reqConfig.ContentType, request, reqConfig.Body) } // formatHeader 格式化header