优化http请求之后对响应结果的处理

1. http状态验证
2. 业务状态码验证
3. 业务数据提取
4. 异常处理统一使用exception库
This commit is contained in:
白茶清欢 2023-08-26 21:31:09 +08:00
parent 4a00f91d87
commit 89e73cc9d9
5 changed files with 107 additions and 73 deletions

1
go.mod
View File

@ -13,6 +13,7 @@ require (
require (
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20230811030300-6f850372c88c // indirect
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20230307094841-e437ba87af10 // indirect
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20230819024237-f674702ad28d // indirect
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20230811032817-e6ad534a9a10 // indirect
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230811055014-2e36e7b1ac67 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect

2
go.sum
View File

@ -2,6 +2,8 @@ git.zhangdeman.cn/zhangdeman/consts v0.0.0-20230811030300-6f850372c88c h1:Dan3iS
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20230811030300-6f850372c88c/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20230307094841-e437ba87af10 h1:+Lg4vXFEiWVKjhUJdXuoP0AgjGT49oqJ3301STnZErk=
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20230307094841-e437ba87af10/go.mod h1:+Lc0zYF8sylRi75A7NGmObrLxugwAZa8WVpWh2eh5X0=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20230819024237-f674702ad28d h1:XqjAykNMm/ai1+CebqcxBKXmIQqZNyYmTeIufLTXQJc=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20230819024237-f674702ad28d/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20230811032817-e6ad534a9a10 h1:orhcMAKrcOajsBJCgssnb9O8YcLsPJvWuXF511gs5dc=
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20230811032817-e6ad534a9a10/go.mod h1:CzX5/WwGDTnKmewarnjkK5XcSRbgszTQTdTL3OUc/s4=
git.zhangdeman.cn/zhangdeman/util v0.0.0-20230811055014-2e36e7b1ac67 h1:risi3/+SuH+snq/+/1dvqDrNyiclCi5weHMixg7IgO8=

View File

@ -12,7 +12,7 @@
package httpclient
import (
"git.zhangdeman.cn/zhangdeman/wrapper"
"git.zhangdeman.cn/zhangdeman/exception"
"github.com/ddliu/go-httpclient"
)
@ -41,6 +41,21 @@ const (
DefaultConnectTimeout = 1000
// DefaultReadTimeout 默认读取超时时间, 毫秒
DefaultReadTimeout = 1000
// DefaultResponseCodeField 默认code字段
DefaultResponseCodeField = "code"
// DefaultResponseMessageField 默认message字段
DefaultResponseMessageField = "message"
// DefaultResponseDataField 默认数据字段
DefaultResponseDataField = "data"
// DefaultMessage 接口响应失败
DefaultMessage = "api request fail"
)
var (
// DefaultSuccessCode 默认成功状态码
DefaultSuccessCode = []string{"0", "200"}
// DefaultSuccessHttpCode 默认成功http状态码
DefaultSuccessHttpCode = []string{"200"}
)
const (
@ -99,10 +114,10 @@ type Timeout struct {
type ApiResponse struct {
RequestConfig *ApiRequestConfig `json:"request_config"` // 请求配置
Response *httpclient.Response `json:"response"` // 响应体
Exception *Exception `json:"exception"` // 异常信息
Exception exception.IException `json:"-"` // 异常信息
StartRequestTime int64 `json:"start_request_time"` // 开始请求时间, 纳秒
FinishRequestTime int64 `json:"finish_request_time"` // 完成请求时间,纳秒
Code string `json:"code"` // 业务状态码
Message string `json:"message"` // 状态码描述
Data wrapper.String `json:"data"` // 响应数据
Data string `json:"data"` // 响应数据
}

View File

@ -11,68 +11,17 @@
// Date: 2022/05/01 20:40:08
package httpclient
import "strings"
// Exception 定义异常
//
// Author : go_developer@163.com<白茶清欢>
//
// Description: 对于各种异常的描述
//
// Date: 2022/05/01 20:48:21
type Exception struct {
Code string `json:"code"` // 异常的标识码
Message string `json:"message"` // 异常的描述
}
const (
// BindRouterParamNotFound 绑定到路由的参数不存在
BindRouterParamNotFound = "404001"
// ResponseCodeNotFound 响应结果获取不到状态码字段
ResponseCodeNotFound = "404002"
// ResponseMessageNotFound 响应结果获取不到状态码描述字段
ResponseMessageNotFound = "404003"
ResponseCodeNotFound = 404000000002
// ResponseDataNotFound 响应结果获取不到数据字段
ResponseDataNotFound = "404004"
// DomainInvalid 域名设置错误, 必须是 http:// 或者 https:// 开头
DomainInvalid = "400001"
ResponseDataNotFound = 404000000004
// SendRequestError 请求发送出现异常
SendRequestError = "400002"
SendRequestError = 400000000002
// ReadResponseBodyError 读取响应数据体出现异常
ReadResponseBodyError = "400003"
ReadResponseBodyError = 400000000003
// RequestMethodNotSupport 请求方法不支持
RequestMethodNotSupport = "405001"
RequestMethodNotSupport = 405000000001
// ResponseHttpCodeIsNotSuccess 响应的http状态码非成功
ResponseHttpCodeIsNotSuccess = 500000000001
)
// exceptionTable ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Description: 异常信息表
//
// Date: 2022/05/01 21:02:55
var exceptionTable = map[string]string{
BindRouterParamNotFound: "绑定到路由的参数{{bind_router_param}}不存在, 请确认参数配置",
ResponseCodeNotFound: "基于配置无法获取响应体状态码, 请检查 response_code_field 的配置, 当前配置 {response_code_field}",
ResponseMessageNotFound: "基于配置无法获取响应体状态码, 请检查 response_code_message 的配置, 当前配置 {response_code_message}",
ResponseDataNotFound: "基于配置无法获取响应体状态码, 请检查 {response_code_data} 的配置, 当前配置 {response_code_data}",
DomainInvalid: "api域名配置非法, 必须以 http:// 或者 https:// 开头, 当前配置 {domain}",
RequestMethodNotSupport: "当前请求方法 {method} 不支持, 请核对相关配置",
SendRequestError: "请求发送出现异常, 异常原因 : {real_reason}",
}
// NewException 实力化异常
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:02:07
func NewException(code string, exceptionData map[string]string) *Exception {
mes := exceptionTable[code]
for k, v := range exceptionData {
mes = strings.ReplaceAll(mes, "{"+k+"}", v)
}
return &Exception{
Code: code,
Message: mes,
}
}

View File

@ -20,9 +20,9 @@ import (
"strings"
"time"
"github.com/tidwall/gjson"
"git.zhangdeman.cn/zhangdeman/exception"
"git.zhangdeman.cn/zhangdeman/wrapper"
"github.com/tidwall/gjson"
"github.com/ddliu/go-httpclient"
)
@ -54,7 +54,7 @@ func Request(apiConfig *ApiRequestConfig, header map[string]string, param map[st
default:
return &ApiResponse{
RequestConfig: apiConfig,
Exception: NewException(RequestMethodNotSupport, map[string]string{"method": apiConfig.Method}),
Exception: exception.New(RequestMethodNotSupport, 0, map[string]string{"method": apiConfig.Method}, apiConfig.Method+" : request method is not support"),
Response: nil,
StartRequestTime: time.Now().UnixNano(),
FinishRequestTime: time.Now().UnixNano(),
@ -228,9 +228,26 @@ func send(apiConfig *ApiRequestConfig, header map[string]string) *ApiResponse {
responseByte []byte
)
// 填充默认配置
if len(apiConfig.ResponseCodeFieldLocation) == 0 {
apiConfig.ResponseCodeFieldLocation = ResponseCodeFieldLocationDefault
}
if len(apiConfig.ResponseCodeField) == 0 {
apiConfig.ResponseCodeField = DefaultResponseCodeField
}
if len(apiConfig.ResponseMessageField) == 0 {
apiConfig.ResponseMessageField = DefaultResponseMessageField
}
if len(apiConfig.ResponseDataField) == 0 {
apiConfig.ResponseDataField = DefaultResponseDataField
}
if len(apiConfig.SuccessCodeList) == 0 {
apiConfig.SuccessCodeList = DefaultSuccessCode
}
if len(apiConfig.SuccessHttpCodeList) == 0 {
apiConfig.SuccessHttpCodeList = DefaultSuccessHttpCode
}
response := &ApiResponse{
RequestConfig: apiConfig,
Response: nil,
@ -239,7 +256,7 @@ func send(apiConfig *ApiRequestConfig, header map[string]string) *ApiResponse {
FinishRequestTime: 0,
Code: "",
Message: "",
Data: wrapper.String("{}"),
Data: "",
}
defer func() {
@ -248,26 +265,76 @@ func send(apiConfig *ApiRequestConfig, header map[string]string) *ApiResponse {
client = getHttpClient(apiConfig, header)
if response.Response, err = client.Do(apiConfig.Method, apiConfig.FullURL, nil, bytes.NewReader(apiConfig.Body)); nil != err {
response.Exception = NewException(SendRequestError, map[string]string{"real_reason": err.Error()})
response.Exception = exception.New(SendRequestError, 0, map[string]string{"real_reason": err.Error()}, "http request send fail : "+err.Error())
return response
}
if responseByte, err = io.ReadAll(response.Response.Body); nil != err {
response.Exception = NewException(ReadResponseBodyError, map[string]string{"real_reason": err.Error()})
response.Exception = exception.New(ReadResponseBodyError, response.Response.StatusCode, map[string]string{"real_reason": err.Error()}, "response body read fail : "+err.Error())
return response
}
// 提取响应数据
if response.RequestConfig.ResponseDataField == ResponseBodyAsData {
response.Data = wrapper.String(string(responseByte))
} else {
response.Data = wrapper.String(gjson.GetBytes(responseByte, response.RequestConfig.ResponseDataField).String())
// 判断http状态码是否为成功
isHttpSuccess := false
responseHttpCode := fmt.Sprintf("%v", response.Response.StatusCode)
for _, itemSuccessHttpCode := range response.RequestConfig.SuccessHttpCodeList {
if responseHttpCode == itemSuccessHttpCode {
isHttpSuccess = true
break
}
}
// http请求失败
if !isHttpSuccess {
response.Exception = exception.New(ResponseHttpCodeIsNotSuccess, response.Response.StatusCode, map[string]string{
"real_reason": responseHttpCode + " : response http code is not success",
}, responseHttpCode+" : response http code is not success")
return response
}
// 提取响应错误码
if response.RequestConfig.ResponseCodeFieldLocation == ResponseCodeFieldLocationHeader {
response.Code = response.Response.Header.Get(response.RequestConfig.ResponseCodeField)
} else {
response.Code = gjson.GetBytes(responseByte, response.RequestConfig.ResponseCodeField).String()
businessCode := gjson.GetBytes(responseByte, response.RequestConfig.ResponseCodeField)
if businessCode.Exists() {
response.Code = businessCode.String()
}
}
// 判断是否提取到响应状态码
if len(response.Code) == 0 {
response.Exception = exception.New(ResponseCodeNotFound, response.Response.StatusCode, map[string]string{
"real_reason": "parse response business code fail",
}, fmt.Sprintf("business code location : %v, business code name : %v, parse business code fail", response.RequestConfig.ResponseCodeFieldLocation, response.RequestConfig.ResponseCodeField))
}
// 提取响应文案
response.Message = gjson.GetBytes(responseByte, response.RequestConfig.ResponseMessageField).String()
if len(response.Message) == 0 {
response.Message = DefaultMessage
}
// 判断响应状态码是否成功
isBusinessCodeSuccess := false
for _, itemSuccessCode := range response.RequestConfig.SuccessHttpCodeList {
if itemSuccessCode == response.Code {
isBusinessCodeSuccess = true
break
}
}
if !isBusinessCodeSuccess {
response.Exception = exception.New(ResponseCodeNotFound, response.Response.StatusCode, map[string]string{}, response.Message)
return response
}
// 提取响应数据
if response.RequestConfig.ResponseDataField == ResponseBodyAsData {
response.Data = string(responseByte)
} else {
responseData := gjson.GetBytes(responseByte, response.RequestConfig.ResponseDataField)
if !responseData.Exists() {
response.Exception = exception.New(ResponseDataNotFound, response.Response.StatusCode, map[string]string{}, "response data not found, data field name : "+response.RequestConfig.ResponseDataField)
return response
}
response.Data = responseData.String()
}
return response
}