From 89e73cc9d9fcf31f1dd6cc30fa7889770f437633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Sat, 26 Aug 2023 21:31:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96http=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=B9=8B=E5=90=8E=E5=AF=B9=E5=93=8D=E5=BA=94=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. http状态验证 2. 业务状态码验证 3. 业务数据提取 4. 异常处理统一使用exception库 --- go.mod | 1 + go.sum | 2 + httpclient/define.go | 21 ++++++++-- httpclient/exception.go | 65 ++++------------------------- httpclient/request.go | 91 +++++++++++++++++++++++++++++++++++------ 5 files changed, 107 insertions(+), 73 deletions(-) diff --git a/go.mod b/go.mod index a10e78a..56ae253 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 799fc74..207fd83 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/httpclient/define.go b/httpclient/define.go index 38982e1..0c8a70d 100644 --- a/httpclient/define.go +++ b/httpclient/define.go @@ -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"` // 响应数据 } diff --git a/httpclient/exception.go b/httpclient/exception.go index 590a355..3cfa15f 100644 --- a/httpclient/exception.go +++ b/httpclient/exception.go @@ -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, - } -} diff --git a/httpclient/request.go b/httpclient/request.go index d7239d1..048b834 100644 --- a/httpclient/request.go +++ b/httpclient/request.go @@ -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 }