From 1fe39e0547480a59dad0d38b1cc4f666ab14b4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 7 May 2025 17:31:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=93=8D=E5=BA=94=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8B=E8=87=AA=E9=80=82=E5=BA=94=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- httpclient/abstract/IResponse.go | 2 +- httpclient/implement/response.go | 43 ++++++++++++++++++++++--- httpclient/implement/response_parser.go | 14 ++++++++ httpclient/mesh/define.go | 1 + httpclient/mesh/mesh.go | 3 +- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/httpclient/abstract/IResponse.go b/httpclient/abstract/IResponse.go index 0605571..e46c2d5 100644 --- a/httpclient/abstract/IResponse.go +++ b/httpclient/abstract/IResponse.go @@ -22,7 +22,7 @@ type IResponseParser interface { // IResponse 响应解析 type IResponse interface { // Parse 解析响应 - Parse(reqConfig *define.Request, response *define.Response) + Parse(reqConfig *define.Request, response *define.Response) error // BusinessSuccess 业务状态码是否成功 BusinessSuccess(reqCfg *define.Request, response *define.Response) bool // HttpSuccess http状态码是否成功 diff --git a/httpclient/implement/response.go b/httpclient/implement/response.go index 1a73ba2..89ff91e 100644 --- a/httpclient/implement/response.go +++ b/httpclient/implement/response.go @@ -8,21 +8,23 @@ package implement import ( + "errors" "fmt" + "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/network/httpclient/define" "git.zhangdeman.cn/zhangdeman/serialize" "github.com/tidwall/gjson" + "strings" ) // Response 响应结果解析 type Response struct { } -func (r *Response) Parse(reqConfig *define.Request, response *define.Response) { +func (r *Response) Parse(reqConfig *define.Request, response *define.Response) error { r.fillResponseHeader(response) r.fillResponseCookie(response) - r.fillResponseBody(reqConfig, response) - return + return r.fillResponseBody(reqConfig, response) } // fillResponseHeader 填充响应header @@ -60,8 +62,38 @@ func (r *Response) fillResponseCookie(response *define.Response) { // Author : go_developer@163.com<白茶清欢> // // Date : 21:38 2024/6/5 -func (r *Response) fillResponseBody(reqCfg *define.Request, response *define.Response) { - response.Data = string(response.RestyResponse.Body()) +func (r *Response) fillResponseBody(reqCfg *define.Request, response *define.Response) error { + // 解析响应数据类型 + responseContentType := response.RestyResponse.Header().Get(consts.HeaderKeyContentType.String()) + if responseContentType == "" { + // 返回数据未说明 Content-Type + return errors.New("response content type is empty") + } + typeArr := strings.Split(strings.Split(responseContentType, ";")[0], "/") + responseType := "json" + if len(typeArr) > 0 { + responseType = typeArr[len(typeArr)-1] + } + parser := ResponseParserTable[responseType] + if parser == nil { + // 未读取到的, 不支持解析对应的响应数据 + return errors.New("response type " + responseType + " is not support") + + } + + var ( + err error + res map[string]any + jsonByte []byte + ) + + if err = parser.Unmarshal(response.RestyResponse.Body(), &res); nil != err { + return errors.New("response parse body error :" + err.Error()) + } + if jsonByte, err = parser.MarshalForByte(res); nil != err { + return errors.New("response body Marshal error :" + err.Error()) + } + response.Data = string(jsonByte) response.Code = gjson.Get(response.Data, reqCfg.CodeField).String() response.Message = gjson.Get(response.Data, reqCfg.MessageField).String() businessData := gjson.Get(response.Data, reqCfg.DataField) @@ -96,6 +128,7 @@ func (r *Response) fillResponseBody(reqCfg *define.Request, response *define.Res response.ExtendData[key.String()] = value.String() return true }) + return nil } // BusinessSuccess ... diff --git a/httpclient/implement/response_parser.go b/httpclient/implement/response_parser.go index 3e14fc7..ddde7f4 100644 --- a/httpclient/implement/response_parser.go +++ b/httpclient/implement/response_parser.go @@ -13,6 +13,10 @@ import ( ) // ResponseParserTable 响应数据解析器 +// 此处为内置解析实现, 未加锁, 未做并发安全处理 +// 原因: 无论 Add 还是 Delete, 均应在服务初始化阶段调用, 而在服务完成初始化, 完全启动之后, 应该只有读操作 +// 如果强要运行时, 动态操作此配置表, 需在外部调用处自行加锁 +// 也可完全自行实现相关解析实例, 在请求时通过 RequestOption 传入, 则控制权完全贵开发者所有 var ResponseParserTable = map[string]abstract.IResponseParser{ "json": serialize.JSON, "xml": serialize.Xml, @@ -21,3 +25,13 @@ var ResponseParserTable = map[string]abstract.IResponseParser{ "yaml": serialize.Yml, "text": serialize.JSON, } + +// AddResponseParser 新增一个解析实现 +func AddResponseParser(t string, parser abstract.IResponseParser) { + ResponseParserTable[t] = parser +} + +// RemoveResponseParser 移除一个解析实现 +func RemoveResponseParser(t string) { + delete(ResponseParserTable, t) +} diff --git a/httpclient/mesh/define.go b/httpclient/mesh/define.go index 3cb40fa..67ce458 100644 --- a/httpclient/mesh/define.go +++ b/httpclient/mesh/define.go @@ -39,6 +39,7 @@ type RequestConfigGroupItem struct { FailBehavior *RequestConfigGroupItemFailBehavior `json:"fail_behavior"` // 失败的行为, 不配置, 默认失败break FinalFailureAllow bool `json:"final_failure_allow"` // 已经确定当前请求是最终失败了,当前请求是否允许执行 CacheInstance abstract.ICache `json:"-"` // 数据缓存实例 + ResponseParser abstract.IResponse `json:"-"` // 响应数据解析 Condition any `json:"condition"` // TODO: 请求条件, 特定条件下不执行当前请求 } diff --git a/httpclient/mesh/mesh.go b/httpclient/mesh/mesh.go index 40201b6..c9e7199 100644 --- a/httpclient/mesh/mesh.go +++ b/httpclient/mesh/mesh.go @@ -148,7 +148,8 @@ func (c *client) doRequest(apiList []*RequestConfigGroupItem) bool { apiCfg.RequestCfg.Cookie = param[strings.ToLower(consts.RequestDataLocationCookie.String())] // cookie apiCfg.RequestCfg.Query = param[strings.ToLower(consts.RequestDataLocationQuery.String())] // query if httpClient, err = httpclient.NewHttpClient(apiCfg.RequestCfg, &httpclient.RequestOption{ - CacheInstance: apiCfg.CacheInstance, + CacheInstance: apiCfg.CacheInstance, + ResponseParser: apiCfg.ResponseParser, }); nil != err { // 此处获取客户端实例即发生异常, 忽略一切配置, 直接作为全局失败, 后续也不请求了 c.resp.ErrorCode = "-500"