响应自适应解析 + 逻辑优化 #8
30
httpclient/abstract/IResponse.go
Normal file
30
httpclient/abstract/IResponse.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Package abstract ...
|
||||
//
|
||||
// Description : abstract ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-05-07 14:28
|
||||
package abstract
|
||||
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/network/httpclient/define"
|
||||
)
|
||||
|
||||
// IResponseParser 响应结果的序列化解析器
|
||||
type IResponseParser interface {
|
||||
// Unmarshal 反序列化解析
|
||||
Unmarshal(byteData []byte, receiver any) error
|
||||
// MarshalForByte 序列化
|
||||
MarshalForByte(input any) ([]byte, error)
|
||||
}
|
||||
|
||||
// IResponse 响应解析
|
||||
type IResponse interface {
|
||||
// Parse 解析响应
|
||||
Parse(reqConfig *define.Request, response *define.Response)
|
||||
// BusinessSuccess 业务状态码是否成功
|
||||
BusinessSuccess(reqCfg *define.Request, response *define.Response) bool
|
||||
// HttpSuccess http状态码是否成功
|
||||
HttpSuccess(reqCfg *define.Request, response *define.Response) bool
|
||||
}
|
@ -9,6 +9,7 @@ package httpclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/network/httpclient/implement"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
@ -19,7 +20,6 @@ import (
|
||||
"git.zhangdeman.cn/zhangdeman/network/httpclient/validate"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// NewHttpClient 获取http client
|
||||
@ -28,6 +28,13 @@ import (
|
||||
//
|
||||
// Date : 15:27 2024/5/31
|
||||
func NewHttpClient(reqConfig *define.Request, reqOption *RequestOption) (*HttpClient, error) {
|
||||
if nil == reqOption {
|
||||
reqOption = &RequestOption{}
|
||||
}
|
||||
if nil == reqOption.ResponseParser {
|
||||
// 没有自定义响应解析实现, 使用内置实现
|
||||
reqOption.ResponseParser = &implement.Response{}
|
||||
}
|
||||
if nil == reqConfig.Logger {
|
||||
reqConfig.Logger = log.Get() // 未单独指定日志实例, 则使用全局日志实例
|
||||
}
|
||||
@ -420,11 +427,9 @@ func (hc *HttpClient) requestBackendApi() *define.Response {
|
||||
continue
|
||||
}
|
||||
// 解析返回信息
|
||||
hc.fillResponseHeader(response)
|
||||
hc.fillResponseCookie(response)
|
||||
hc.fillResponseBody(response)
|
||||
hc.reqOption.ResponseParser.Parse(hc.reqCfg, response)
|
||||
// 配置了当前code为成功, 或者未配置任何code, 当前code为2xx, 则认为请求成功
|
||||
isHttpSuccess := successHttpCodeTable[response.HttpCode] || (len(successHttpCodeTable) == 0 && response.HttpCode/100 == 2)
|
||||
isHttpSuccess := hc.reqOption.ResponseParser.HttpSuccess(hc.reqCfg, response)
|
||||
if !isHttpSuccess {
|
||||
// 非 成功
|
||||
errType := define.RequestFailTypeServerError
|
||||
@ -450,7 +455,7 @@ func (hc *HttpClient) requestBackendApi() *define.Response {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !hc.isCodeSuccess(response) {
|
||||
if !hc.reqOption.ResponseParser.BusinessSuccess(hc.reqCfg, response) {
|
||||
response.FailInfo = &define.ResponseFailInfo{
|
||||
Type: define.RequestFailTypeBusinessError,
|
||||
Message: "business code is " + response.Code + ", not success",
|
||||
@ -517,93 +522,6 @@ func (hc *HttpClient) newResponse() *define.Response {
|
||||
}
|
||||
}
|
||||
|
||||
// fillResponseHeader 填充响应header
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:30 2024/6/5
|
||||
func (hc *HttpClient) fillResponseHeader(response *define.Response) {
|
||||
response.Header = map[string]any{} // 清空已有数据
|
||||
response.HttpCode = response.RestyResponse.StatusCode() // http状态码
|
||||
response.HttpCodeStatus = response.RestyResponse.Status() // http状态码描述
|
||||
for headerName, headerValue := range response.RestyResponse.Header() {
|
||||
if len(headerValue) > 0 {
|
||||
response.Header[headerName] = headerValue[0]
|
||||
} else {
|
||||
response.Header[headerName] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fillResponseCookie 填充cookie
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:32 2024/6/5
|
||||
func (hc *HttpClient) fillResponseCookie(response *define.Response) {
|
||||
response.Cookie = map[string]any{} // 清空已有数据
|
||||
for _, cookieValue := range response.RestyResponse.Cookies() {
|
||||
response.Cookie[cookieValue.Name] = cookieValue.Value
|
||||
}
|
||||
}
|
||||
|
||||
// fillResponseBody 填充响应body
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:38 2024/6/5
|
||||
func (hc *HttpClient) fillResponseBody(response *define.Response) {
|
||||
response.Data = string(response.RestyResponse.Body())
|
||||
response.Code = gjson.Get(response.Data, hc.reqCfg.CodeField).String()
|
||||
response.Message = gjson.Get(response.Data, hc.reqCfg.MessageField).String()
|
||||
businessData := gjson.Get(response.Data, hc.reqCfg.DataField)
|
||||
if businessData.Value() == nil {
|
||||
// data为空指针, 归一化成空对象
|
||||
response.Body = map[string]any{}
|
||||
} else {
|
||||
if businessData.IsArray() {
|
||||
// 数组类型的转换
|
||||
response.Data = fmt.Sprintf(`{"list":` + businessData.String() + "}")
|
||||
} else {
|
||||
if businessData.IsObject() {
|
||||
// 返回的就是对象
|
||||
response.Data = businessData.String()
|
||||
} else {
|
||||
// 返回是普通类型
|
||||
response.Data = serialize.JSON.MarshalForStringIgnoreError(map[string]any{
|
||||
"value": businessData.Value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
_ = serialize.JSON.UnmarshalWithNumber([]byte(response.Data), &response.Body)
|
||||
}
|
||||
|
||||
response.ExtendData = map[string]string{}
|
||||
gjson.Parse(response.Data).ForEach(func(key, value gjson.Result) bool {
|
||||
if key.String() == hc.reqCfg.CodeField ||
|
||||
key.String() == hc.reqCfg.MessageField ||
|
||||
key.String() == hc.reqCfg.DataField {
|
||||
return true
|
||||
}
|
||||
response.ExtendData[key.String()] = value.String()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// isHttpCodeSuccess ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:48 2024/6/6
|
||||
func (hc *HttpClient) isCodeSuccess(response *define.Response) bool {
|
||||
for _, itemSuccessCode := range hc.reqCfg.SuccessCodeList {
|
||||
if itemSuccessCode == response.Code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getCacheResult 获取缓存结果
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
|
127
httpclient/implement/response.go
Normal file
127
httpclient/implement/response.go
Normal file
@ -0,0 +1,127 @@
|
||||
// Package implement ...
|
||||
//
|
||||
// Description : implement ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-05-07 14:56
|
||||
package implement
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/network/httpclient/define"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// Response 响应结果解析
|
||||
type Response struct {
|
||||
}
|
||||
|
||||
func (r *Response) Parse(reqConfig *define.Request, response *define.Response) {
|
||||
r.fillResponseHeader(response)
|
||||
r.fillResponseCookie(response)
|
||||
r.fillResponseBody(reqConfig, response)
|
||||
return
|
||||
}
|
||||
|
||||
// fillResponseHeader 填充响应header
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:30 2024/6/5
|
||||
func (r *Response) fillResponseHeader(response *define.Response) {
|
||||
response.Header = map[string]any{} // 清空已有数据
|
||||
response.HttpCode = response.RestyResponse.StatusCode() // http状态码
|
||||
response.HttpCodeStatus = response.RestyResponse.Status() // http状态码描述
|
||||
for headerName, headerValue := range response.RestyResponse.Header() {
|
||||
if len(headerValue) > 0 {
|
||||
response.Header[headerName] = headerValue[0]
|
||||
} else {
|
||||
response.Header[headerName] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fillResponseCookie 填充cookie
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:32 2024/6/5
|
||||
func (r *Response) fillResponseCookie(response *define.Response) {
|
||||
response.Cookie = map[string]any{} // 清空已有数据
|
||||
for _, cookieValue := range response.RestyResponse.Cookies() {
|
||||
response.Cookie[cookieValue.Name] = cookieValue.Value
|
||||
}
|
||||
}
|
||||
|
||||
// fillResponseBody 填充响应body
|
||||
//
|
||||
// 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())
|
||||
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)
|
||||
if businessData.Value() == nil {
|
||||
// data为空指针, 归一化成空对象
|
||||
response.Body = map[string]any{}
|
||||
} else {
|
||||
if businessData.IsArray() {
|
||||
// 数组类型的转换
|
||||
response.Data = fmt.Sprintf(`{"list":` + businessData.String() + "}")
|
||||
} else {
|
||||
if businessData.IsObject() {
|
||||
// 返回的就是对象
|
||||
response.Data = businessData.String()
|
||||
} else {
|
||||
// 返回是普通类型
|
||||
response.Data = serialize.JSON.MarshalForStringIgnoreError(map[string]any{
|
||||
"value": businessData.Value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
_ = serialize.JSON.UnmarshalWithNumber([]byte(response.Data), &response.Body)
|
||||
}
|
||||
|
||||
response.ExtendData = map[string]string{}
|
||||
gjson.Parse(response.Data).ForEach(func(key, value gjson.Result) bool {
|
||||
if key.String() == reqCfg.CodeField ||
|
||||
key.String() == reqCfg.MessageField ||
|
||||
key.String() == reqCfg.DataField {
|
||||
return true
|
||||
}
|
||||
response.ExtendData[key.String()] = value.String()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// BusinessSuccess ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:48 2024/6/6
|
||||
func (r *Response) BusinessSuccess(reqCfg *define.Request, response *define.Response) bool {
|
||||
for _, itemSuccessCode := range reqCfg.SuccessCodeList {
|
||||
if itemSuccessCode == response.Code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HttpSuccess ...
|
||||
func (r *Response) HttpSuccess(reqCfg *define.Request, response *define.Response) bool {
|
||||
if len(reqCfg.SuccessHttpCodeList) == 0 {
|
||||
// 没配置, 则 2xx 均视为成功
|
||||
return response.HttpCode/100 == 2
|
||||
}
|
||||
for _, itemHttpCode := range reqCfg.SuccessHttpCodeList {
|
||||
if itemHttpCode == response.HttpCode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
23
httpclient/implement/response_parser.go
Normal file
23
httpclient/implement/response_parser.go
Normal file
@ -0,0 +1,23 @@
|
||||
// Package implement ...
|
||||
//
|
||||
// Description : implement ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-05-07 14:53
|
||||
package implement
|
||||
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/network/httpclient/abstract"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
)
|
||||
|
||||
// ResponseParserTable 响应数据解析器
|
||||
var ResponseParserTable = map[string]abstract.IResponseParser{
|
||||
"json": serialize.JSON,
|
||||
"xml": serialize.Xml,
|
||||
"yml": serialize.Yml,
|
||||
"toml": serialize.Toml,
|
||||
"yaml": serialize.Yml,
|
||||
"text": serialize.JSON,
|
||||
}
|
@ -13,5 +13,6 @@ import (
|
||||
|
||||
// RequestOption 请求一些选项
|
||||
type RequestOption struct {
|
||||
CacheInstance abstract.ICache `json:"-"` // 数据结果缓存实例
|
||||
CacheInstance abstract.ICache `json:"-"` // 数据结果缓存实例
|
||||
ResponseParser abstract.IResponse `json:"-"` // 返回结果解析, 不配置使用内置实现
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user