From 2be9c90b79f9a6eb29aae9cd52a28a91e431dfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 31 May 2024 16:39:58 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=B7=E6=B1=82=E9=85=8D=E7=BD=AE=E9=AA=8C?= =?UTF-8?q?=E8=AF=81,=20=E9=BB=98=E8=AE=A4=E5=80=BC=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=20+=20full=5Furl=20+=20method=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.go | 90 +++++++++++++++++++++++++++++ define/error.go | 17 ++++++ define/request.go | 37 ++++++++---- go.mod | 3 +- resty.go | 44 ++++++++++++++ validate/request_config.go | 114 +++++++++++++++++++++++++++++++++++++ 6 files changed, 293 insertions(+), 12 deletions(-) create mode 100644 client.go create mode 100644 define/error.go create mode 100644 resty.go create mode 100644 validate/request_config.go diff --git a/client.go b/client.go new file mode 100644 index 0000000..079ddcf --- /dev/null +++ b/client.go @@ -0,0 +1,90 @@ +// Package httpclient ... +// +// Description : httpclient ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-05-31 15:22 +package httpclient + +import ( + "git.zhangdeman.cn/gateway/httpclient/define" + "git.zhangdeman.cn/gateway/httpclient/validate" + "github.com/go-resty/resty/v2" +) + +// NewHttpClient 获取http client +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:27 2024/5/31 +func NewHttpClient(reqConfig *define.Request) (*HttpClient, error) { + hc := &HttpClient{ + Client: NewRestyClient(reqConfig), + reqConfig: reqConfig, + } + // 验证配置正确性以及初始化默认值 + if err := validate.RequestConfig(reqConfig); nil != err { + return nil, err + } + return hc, nil +} + +// HttpClient 请求客户端 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:27 2024/5/31 +type HttpClient struct { + *resty.Client + reqConfig *define.Request +} + +// getRequestValidateMiddleware 请求验证的Middleware +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:40 2024/5/31 +func (hc *HttpClient) getRequestValidateMiddleware() resty.RequestMiddleware { + return func(client *resty.Client, request *resty.Request) error { + return nil + } +} + +// getResponseValidateMiddleware 获取相应数据验证的middleware +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:42 2024/5/31 +func (hc *HttpClient) getResponseValidateMiddleware() resty.ResponseMiddleware { + return func(client *resty.Client, response *resty.Response) error { + return nil + } +} + +// SetRestyClient 设置client +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:54 2024/5/31 +func (hc *HttpClient) SetRestyClient(restyClient *resty.Client) { + hc.Client = restyClient +} + +// GetRestyClient 获取 resty client +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:57 2024/5/31 +func (hc *HttpClient) GetRestyClient() *resty.Client { + return hc.Client +} + +// Request 发送请求 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:52 2024/5/31 +func (hc *HttpClient) Request(reqConfig *define.Request) *define.Response { + return nil +} diff --git a/define/error.go b/define/error.go new file mode 100644 index 0000000..e59378f --- /dev/null +++ b/define/error.go @@ -0,0 +1,17 @@ +// Package define ... +// +// Description : define ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-05-31 16:02 +package define + +import "errors" + +var ( + ErrRequestConfigNil = errors.New("REQUEST_CONFIG_NIL") // 请求配置 nil + ErrFullUrlEmpty = errors.New("FULL_URL_EMPTY") // 没传 full_url + ErrFullUrlInvalid = errors.New("FULL_URL_Invalid") // 请求 full_url 不是 http 或者 https 开头 + ErrMethodIsNotSupport = errors.New("METHOD_IS_NOT_SUPPORT") // 请求 method不支持 +) diff --git a/define/request.go b/define/request.go index 21090c7..fc89126 100644 --- a/define/request.go +++ b/define/request.go @@ -13,17 +13,20 @@ package define // // Date : 17:10 2024/5/24 type Request struct { - Body map[string]any `json:"body"` // 请求Body - Header map[string]string `json:"header"` // 请求Header - Cookie map[string]string `json:"cookie"` // 请求Cookie - Query map[string]string `json:"query"` // 请求query - FullUrl string `json:"full_url"` // 完整的请求URL - ContentType string `json:"content_type"` // 请求类型 - Method string `json:"method"` // 请求方法 - DataField string `json:"data_field"` // 数据字段 - CodeField string `json:"code_field"` // 业务状态码字段 - MessageField string `json:"message_field"` // code描述字段 - RetryRule *RequestRetryRule `json:"retry_rule"` // 重试规则 + Body map[string]any `json:"body"` // 请求Body + Header map[string]string `json:"header"` // 请求Header + Cookie map[string]string `json:"cookie"` // 请求Cookie + Query map[string]string `json:"query"` // 请求query + FullUrl string `json:"full_url"` // 完整的请求URL + ContentType string `json:"content_type"` // 请求类型 + Method string `json:"method"` // 请求方法 + DataField string `json:"data_field"` // 数据字段 + CodeField string `json:"code_field"` // 业务状态码字段 + MessageField string `json:"message_field"` // code描述字段 + SuccessCodeList []string `json:"success_code_list"` // 哪些业务状态码视为成功 + ConnectTimeout int64 `json:"connect_timeout"` // 连接超时时间: ms + ReadTimeout int64 `json:"read_timeout"` // 读取超时时间 + RetryRule *RequestRetryRule `json:"retry_rule"` // 重试规则 } // RequestRetryRule 重试规则 @@ -37,3 +40,15 @@ type RequestRetryRule struct { RetryHttpCodeList []int64 `json:"retry_http_code_list"` // 哪些http状态码需要重试 RetryBusinessCodeList []string `json:"retry_business_code_list"` // 哪些业务状态码需要重试 } + +const ( + DefaultConnectTimeout = 1000 // 默认连接超时时间 + DefaultReadTimeout = 1000 // 默认连接读取时间 + DefaultCodeField = "code" // 默认业务状态码字段 + DefaultMessageField = "message" // 默认状态码描述字段 + DefaultDataField = "data" // 默认数据字段 +) + +var ( + DefaultSuccessCodeList = []string{"0"} // 默认成功业务状态码 +) diff --git a/go.mod b/go.mod index 403b5fb..24b9d0c 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module git.zhangdeman.cn/gateway/httpclient go 1.22.3 +require github.com/go-resty/resty/v2 v2.13.1 + require ( git.zhangdeman.cn/gateway/validator v0.0.0-20240517061043-10dc8547cc14 // indirect git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240517060653-203cc568fbec // indirect @@ -13,7 +15,6 @@ require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-resty/resty/v2 v2.13.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mozillazg/go-pinyin v0.20.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect diff --git a/resty.go b/resty.go new file mode 100644 index 0000000..dff4ac5 --- /dev/null +++ b/resty.go @@ -0,0 +1,44 @@ +// Package httpclient ... +// +// Description : httpclient ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-05-31 14:59 +package httpclient + +import ( + "git.zhangdeman.cn/gateway/httpclient/define" + "github.com/go-resty/resty/v2" + "net/textproto" +) + +// NewRestyClient 获取resty client +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:00 2024/5/31 +func NewRestyClient(requestConfig *define.Request) *resty.Client { + formatHeader(requestConfig) + client := resty.New() + if nil == requestConfig { + return client + } + return client +} + +// formatHeader 格式化header +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 15:18 2024/5/31 +func formatHeader(requestConfig *define.Request) { + if nil == requestConfig { + return + } + formatHeaderData := make(map[string]string) + for headerName, headerVal := range requestConfig.Header { + formatHeaderData[textproto.CanonicalMIMEHeaderKey(headerName)] = headerVal + } + requestConfig.Header = formatHeaderData +} diff --git a/validate/request_config.go b/validate/request_config.go new file mode 100644 index 0000000..14513a2 --- /dev/null +++ b/validate/request_config.go @@ -0,0 +1,114 @@ +// Package validate ... +// +// Description : validate ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-05-31 16:18 +package validate + +import ( + "git.zhangdeman.cn/gateway/httpclient/define" + "net/http" + "strings" +) + +// RequestConfig 验证请求配置 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 16:18 2024/5/31 +func RequestConfig(reqConfig *define.Request) error { + if nil == reqConfig { + return define.ErrRequestConfigNil + } + rc := &requestConfig{} + if err := rc.validateFullUrl(reqConfig); nil != err { + return err + } + return nil +} + +type requestConfig struct { +} + +// initDefaultConfig 初始化默认配置 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 16:25 2024/5/31 +func (rc *requestConfig) initDefaultConfig(reqConfig *define.Request) { + if reqConfig.ConnectTimeout <= 0 { + reqConfig.ConnectTimeout = define.DefaultConnectTimeout + } + if reqConfig.ReadTimeout <= 0 { + reqConfig.ReadTimeout = define.DefaultReadTimeout + } + reqConfig.CodeField = strings.TrimSpace(reqConfig.CodeField) + reqConfig.MessageField = strings.TrimSpace(reqConfig.MessageField) + reqConfig.DataField = strings.TrimSpace(reqConfig.DataField) + if len(reqConfig.CodeField) == 0 { + reqConfig.CodeField = define.DefaultCodeField + } + if len(reqConfig.MessageField) == 0 { + reqConfig.MessageField = define.DefaultMessageField + } + if len(reqConfig.DataField) == 0 { + reqConfig.DataField = define.DefaultDataField + } + if len(reqConfig.SuccessCodeList) == 0 { + reqConfig.SuccessCodeList = define.DefaultSuccessCodeList + } +} + +// validateFullUrl 验证full url +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 16:17 2024/5/31 +func (rc *requestConfig) validateFullUrl(reqConfig *define.Request) error { + // 验证 full url + reqConfig.FullUrl = strings.TrimSpace(reqConfig.FullUrl) + if len(reqConfig.FullUrl) == 0 { + return define.ErrFullUrlEmpty + } + if !strings.HasPrefix(reqConfig.FullUrl, "http://") && !strings.HasPrefix(reqConfig.FullUrl, "https://") { + return define.ErrFullUrlInvalid + } + return nil +} + +// validateMethod 验证method +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 16:22 2024/5/31 +func (rc *requestConfig) validateMethod(reqConfig *define.Request) error { + // 验证Method + reqConfig.Method = strings.ToUpper(reqConfig.Method) + if len(reqConfig.Method) == 0 { + return define.ErrFullUrlEmpty + } + supportMethodList := []string{ + http.MethodGet, + http.MethodHead, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + http.MethodConnect, + http.MethodOptions, + http.MethodTrace, + } + isSupportMethod := false + for _, item := range supportMethodList { + if item == reqConfig.Method { + isSupportMethod = true + break + } + } + if !isSupportMethod { + return define.ErrMethodIsNotSupport + } + return nil +}