httpclient支持mesh请求 #2
17
httpclient/README.md
Normal file
17
httpclient/README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# httpclient
|
||||||
|
|
||||||
|
## 库作用
|
||||||
|
|
||||||
|
- 对第三方接口发起请求
|
||||||
|
|
||||||
|
## 依赖基础库
|
||||||
|
|
||||||
|
- [resty](https://github.com/go-resty/resty)
|
||||||
|
|
||||||
|
## 配置说明
|
||||||
|
|
||||||
|
## 默认配置
|
||||||
|
|
||||||
|
- AllowGetMethodPayload : 允许GET请求带Body, 默认值 **`true`** , 如需关闭, 可通过 HttpClient.GetRestyClient().SetAllowGetMethodPayload(false) 关闭
|
||||||
|
|
||||||
|
## 使用方式
|
66
httpclient/cache/ICache.go
vendored
Normal file
66
httpclient/cache/ICache.go
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Package cache ...
|
||||||
|
//
|
||||||
|
// Description : cache ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-06-03 15:49
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import "git.zhangdeman.cn/gateway/httpclient/define"
|
||||||
|
|
||||||
|
// ICache 缓存定义
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:49 2024/6/3
|
||||||
|
type ICache interface {
|
||||||
|
// Enable 是否启用缓存(总开关)
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:50 2024/6/3
|
||||||
|
Enable() bool
|
||||||
|
// CacheTime 缓存时长, 单位 : s , 默认 1800, 最小值 90, 设置失效时间, 会上下波动60s, 避免缓存集中失效
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:50 2024/6/3
|
||||||
|
CacheTime() int64
|
||||||
|
// IsAllow 针对当前请求数据和状态, 是否允许缓存
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:52 2024/6/3
|
||||||
|
IsAllow(reqCfg *define.Request, response *define.Response) bool
|
||||||
|
// GetKey 获取缓存key
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:51 2024/6/3
|
||||||
|
GetKey(reqCfg *define.Request) string
|
||||||
|
// GetValue 获取缓存值
|
||||||
|
//
|
||||||
|
// Author : zhangdeman001@ke.com<张德满>
|
||||||
|
//
|
||||||
|
// Date : 16:01 2024/6/3
|
||||||
|
GetValue(cacheKey string) string
|
||||||
|
// SetValue 设置缓存
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:46 2024/6/3
|
||||||
|
SetValue(cacheKey string, cacheValue string) error
|
||||||
|
// TTL 缓存剩余生命周期(单位: s)
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:39 2024/10/9
|
||||||
|
TTL(cacheKey string) int64
|
||||||
|
// PreHeatConfig 缓存预热配置
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:42 2024/10/9
|
||||||
|
PreHeatConfig() *define.CachePreHeatConfig
|
||||||
|
}
|
431
httpclient/client.go
Normal file
431
httpclient/client.go
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
// Package httpclient ...
|
||||||
|
//
|
||||||
|
// Description : httpclient ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-05-31 15:22
|
||||||
|
package httpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.zhangdeman.cn/gateway/httpclient/cache"
|
||||||
|
"git.zhangdeman.cn/gateway/httpclient/define"
|
||||||
|
"git.zhangdeman.cn/gateway/httpclient/validate"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHttpClient 获取http client
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:27 2024/5/31
|
||||||
|
func NewHttpClient(reqConfig *define.Request, cacheInstance cache.ICache) (*HttpClient, error) {
|
||||||
|
// 验证配置正确性以及初始化默认值
|
||||||
|
if err := validate.RequestConfig(reqConfig); nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
restyClient, restyRequest := NewRestyClient(reqConfig)
|
||||||
|
hc := &HttpClient{
|
||||||
|
Client: restyClient,
|
||||||
|
request: restyRequest,
|
||||||
|
reqConfig: reqConfig,
|
||||||
|
http4xxHandler: make([]define.Http4xxHandler, 0),
|
||||||
|
http5xxHandler: make([]define.Http5xxHandler, 0),
|
||||||
|
httpBusinessErrorHandler: make([]define.HttpBusinessErrorHandler, 0),
|
||||||
|
requestFinishHandler: make([]define.RequestFinishHandler, 0),
|
||||||
|
cacheInstance: cacheInstance,
|
||||||
|
}
|
||||||
|
return hc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpClient 请求客户端
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:27 2024/5/31
|
||||||
|
type HttpClient struct {
|
||||||
|
*resty.Client
|
||||||
|
request *resty.Request
|
||||||
|
reqConfig *define.Request
|
||||||
|
http4xxHandler []define.Http4xxHandler
|
||||||
|
http5xxHandler []define.Http5xxHandler
|
||||||
|
httpBusinessErrorHandler []define.HttpBusinessErrorHandler
|
||||||
|
requestSendErrorHandler []define.RequestSendErrorHandler
|
||||||
|
requestFinishHandler []define.RequestFinishHandler
|
||||||
|
cacheInstance cache.ICache
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnResponse4xx 4xx处理逻辑
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:20 2024/6/1
|
||||||
|
func (hc *HttpClient) OnResponse4xx(handlerList ...define.Http4xxHandler) {
|
||||||
|
hc.http4xxHandler = append(hc.http4xxHandler, handlerList...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnResponse5xx 5xx处理逻辑
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:21 2024/6/1
|
||||||
|
func (hc *HttpClient) OnResponse5xx(handlerList ...define.Http5xxHandler) {
|
||||||
|
hc.http5xxHandler = append(hc.http5xxHandler, handlerList...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnResponseBusinessError 业务错误出路逻辑
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:22 2024/6/1
|
||||||
|
func (hc *HttpClient) OnResponseBusinessError(handlerList ...define.HttpBusinessErrorHandler) {
|
||||||
|
hc.httpBusinessErrorHandler = append(hc.httpBusinessErrorHandler, handlerList...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRequestFinish 请求完成时间
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:36 2024/6/1
|
||||||
|
func (hc *HttpClient) OnRequestFinish(handlerList ...define.RequestFinishHandler) {
|
||||||
|
hc.requestFinishHandler = append(hc.requestFinishHandler, handlerList...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() *define.Response {
|
||||||
|
var (
|
||||||
|
cacheResult *define.Response
|
||||||
|
)
|
||||||
|
|
||||||
|
if cacheResult = hc.getCacheResult(); nil != cacheResult {
|
||||||
|
// 判断是否开启预热
|
||||||
|
inputCachePreHeatConfig := hc.cacheInstance.PreHeatConfig()
|
||||||
|
var cachePreHeatConfig define.CachePreHeatConfig
|
||||||
|
serialize.JSON.TransitionIgnoreError(inputCachePreHeatConfig, &cachePreHeatConfig)
|
||||||
|
if !cachePreHeatConfig.Enable || (cachePreHeatConfig.MinTTL <= 0 && cachePreHeatConfig.MinPercent <= 0 && !cachePreHeatConfig.Force) {
|
||||||
|
// 无预热配置或未启用预热或者未设置预热规则
|
||||||
|
return cacheResult
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
// 判断是否触发预热
|
||||||
|
if cachePreHeatConfig.Force {
|
||||||
|
_ = hc.requestBackendApi()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 将百分比的配置归一化成最小剩余时间的配置
|
||||||
|
if cachePreHeatConfig.MinPercent > 0 {
|
||||||
|
expectMinTTL := hc.cacheInstance.CacheTime() * cachePreHeatConfig.MinPercent / 100
|
||||||
|
if cachePreHeatConfig.MinTTL == 0 || cachePreHeatConfig.MinTTL > expectMinTTL {
|
||||||
|
cachePreHeatConfig.MinTTL = expectMinTTL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cachePreHeatConfig.MinTTL <= 0 {
|
||||||
|
// 未配置最小剩余时间
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ttl := hc.cacheInstance.TTL(cacheResult.CacheInfo.CacheKey)
|
||||||
|
if ttl < 0 {
|
||||||
|
// 不存在或者未设置有效期
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttl <= cachePreHeatConfig.MinTTL {
|
||||||
|
// 配置了最小剩余时间,并且key剩余有效期小于最小剩余时间
|
||||||
|
_ = hc.requestBackendApi()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return hc.requestBackendApi()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestBackendApi 请求后端接口
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:47 2024/10/9
|
||||||
|
func (hc *HttpClient) requestBackendApi() *define.Response {
|
||||||
|
hc.Client.OnBeforeRequest(hc.getRequestValidateMiddleware()) // 请求参数验证中间件必注册
|
||||||
|
hc.Client.OnAfterResponse(hc.getResponseValidateMiddleware()) // 响应验证中间件必注册
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
response := hc.newResponse()
|
||||||
|
for i := 0; i < hc.reqConfig.RetryRule.RetryCount+1; i++ {
|
||||||
|
response.Seq++
|
||||||
|
response.RequestCount++
|
||||||
|
if response.RestyResponse, err = hc.request.Send(); nil != err {
|
||||||
|
response.FailInfo = &define.ResponseFailInfo{
|
||||||
|
Type: define.RequestFailTypeSend,
|
||||||
|
Message: "response instance is nil",
|
||||||
|
}
|
||||||
|
for _, itemAfterResponse := range hc.requestFinishHandler {
|
||||||
|
itemAfterResponse(hc.reqConfig, response)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(hc.reqConfig.RetryRule.RetryTimeInterval) * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil == response.RestyResponse {
|
||||||
|
response.FailInfo = &define.ResponseFailInfo{
|
||||||
|
Type: define.RequestFailTypeSend,
|
||||||
|
Message: "response instance is nil",
|
||||||
|
}
|
||||||
|
for _, itemAfterResponse := range hc.requestFinishHandler {
|
||||||
|
itemAfterResponse(hc.reqConfig, response)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(hc.reqConfig.RetryRule.RetryTimeInterval) * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 解析返回信息
|
||||||
|
hc.fillResponseHeader(response)
|
||||||
|
hc.fillResponseCookie(response)
|
||||||
|
hc.fillResponseBody(response)
|
||||||
|
if response.HttpCode != http.StatusOK {
|
||||||
|
response.FailInfo = &define.ResponseFailInfo{
|
||||||
|
Type: define.RequestFailTypeServerError,
|
||||||
|
Message: "http code is " + response.HttpCodeStatus + ", not success",
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(hc.reqConfig.RetryRule.RetryTimeInterval) * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !hc.isCodeSuccess(response) {
|
||||||
|
response.FailInfo = &define.ResponseFailInfo{
|
||||||
|
Type: define.RequestFailTypeBusinessError,
|
||||||
|
Message: "business code is " + response.Code + ", not success",
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(hc.reqConfig.RetryRule.RetryTimeInterval) * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.IsSuccess = true //设置成功
|
||||||
|
response.CacheInfo.SetCache, response.CacheInfo.CacheError = hc.setCacheResult(response) // 设置缓存
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// newResponse 默认返回数据
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:44 2024/6/1
|
||||||
|
func (hc *HttpClient) newResponse() *define.Response {
|
||||||
|
return &define.Response{
|
||||||
|
Header: map[string]string{},
|
||||||
|
Cookie: map[string]string{},
|
||||||
|
Body: map[string]any{},
|
||||||
|
Code: "",
|
||||||
|
Message: "",
|
||||||
|
Data: "",
|
||||||
|
HttpCode: 0,
|
||||||
|
HttpCodeStatus: "",
|
||||||
|
ResponseDataRule: nil,
|
||||||
|
Seq: 0,
|
||||||
|
RequestStartTime: 0,
|
||||||
|
RequestFinishTime: 0,
|
||||||
|
UsedTime: 0,
|
||||||
|
RestyResponse: nil,
|
||||||
|
IsSuccess: false,
|
||||||
|
CacheInfo: &define.ResponseCacheInfo{
|
||||||
|
IsCache: false,
|
||||||
|
SetCache: false,
|
||||||
|
CacheKey: "",
|
||||||
|
CacheValue: "",
|
||||||
|
CacheEnable: nil != hc.cacheInstance && hc.cacheInstance.Enable(),
|
||||||
|
CacheError: nil,
|
||||||
|
},
|
||||||
|
RequestCount: 0,
|
||||||
|
FailInfo: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillResponseHeader 填充响应header
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 21:30 2024/6/5
|
||||||
|
func (hc *HttpClient) fillResponseHeader(response *define.Response) {
|
||||||
|
response.Header = map[string]string{} // 清空已有数据
|
||||||
|
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]string{} // 清空已有数据
|
||||||
|
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.reqConfig.CodeField).String()
|
||||||
|
response.Message = gjson.Get(response.Data, hc.reqConfig.MessageField).String()
|
||||||
|
businessData := gjson.Get(response.Data, hc.reqConfig.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.reqConfig.CodeField ||
|
||||||
|
key.String() == hc.reqConfig.MessageField ||
|
||||||
|
key.String() == hc.reqConfig.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.reqConfig.SuccessCodeList {
|
||||||
|
if itemSuccessCode == response.Code {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCacheResult 获取缓存结果
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:04 2024/6/3
|
||||||
|
func (hc *HttpClient) getCacheResult() *define.Response {
|
||||||
|
if nil == hc.cacheInstance || !hc.cacheInstance.Enable() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
startTime := time.Now().UnixMilli()
|
||||||
|
cacheKey := hc.cacheInstance.GetKey(hc.reqConfig)
|
||||||
|
cacheValue := strings.TrimSpace(hc.cacheInstance.GetValue(cacheKey))
|
||||||
|
if len(cacheValue) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
response := hc.newResponse()
|
||||||
|
if err := serialize.JSON.UnmarshalWithNumber([]byte(cacheValue), response); nil != err {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
response.CacheInfo.IsCache = true // 设置缓存标记
|
||||||
|
response.RequestStartTime = startTime // 开始时间
|
||||||
|
response.RequestFinishTime = time.Now().UnixMilli() // 结束时间
|
||||||
|
response.UsedTime = response.RequestFinishTime - response.RequestStartTime // 耗时
|
||||||
|
response.CacheInfo.CacheKey = cacheKey // 缓存key
|
||||||
|
response.CacheInfo.CacheValue = cacheValue // 缓存值
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCacheResult ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:24 2024/6/3
|
||||||
|
func (hc *HttpClient) setCacheResult(response *define.Response) (bool, error) {
|
||||||
|
if nil == response || nil == hc.cacheInstance {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
// 全局未开启或者当前请求不支持缓存
|
||||||
|
if !hc.cacheInstance.Enable() || !hc.cacheInstance.IsAllow(hc.reqConfig, response) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
cacheKey := hc.cacheInstance.GetKey(hc.reqConfig)
|
||||||
|
cacheValue := serialize.JSON.MarshalForStringIgnoreError(response)
|
||||||
|
if err := hc.cacheInstance.SetValue(cacheKey, cacheValue); nil != err {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
response.CacheInfo.CacheKey = cacheKey
|
||||||
|
response.CacheInfo.CacheValue = cacheValue
|
||||||
|
return true, nil
|
||||||
|
}
|
20
httpclient/define/cache.go
Normal file
20
httpclient/define/cache.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Package define ...
|
||||||
|
//
|
||||||
|
// Description : define ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-10-09 18:39
|
||||||
|
package define
|
||||||
|
|
||||||
|
// CachePreHeatConfig 缓存预热配置, MinPercent / MinTTL 同时配置, 则任意一个满足, 均进行预热
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:40 2024/10/9
|
||||||
|
type CachePreHeatConfig struct {
|
||||||
|
Enable bool `json:"enable"` // 缓存预热是否可用
|
||||||
|
MinPercent int64 `json:"min_percent"` // 最小百分比, 剩余有效期低于此百分比进行预热
|
||||||
|
MinTTL int64 `json:"min_ttl"` // 最小剩余生命周期, 低于此百分比进行预热
|
||||||
|
Force bool `json:"force"` // 启用预热的情况下, 强制预热, 会忽略 MinPercent / MinTTL 的配置
|
||||||
|
}
|
17
httpclient/define/error.go
Normal file
17
httpclient/define/error.go
Normal file
@ -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不支持
|
||||||
|
)
|
43
httpclient/define/event.go
Normal file
43
httpclient/define/event.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Package define ...
|
||||||
|
//
|
||||||
|
// Description : define ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-05-31 14:51
|
||||||
|
package define
|
||||||
|
|
||||||
|
// Http4xxHandler 4xx handler
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 14:54 2024/5/31
|
||||||
|
type Http4xxHandler func(req *Request, rep *Response)
|
||||||
|
|
||||||
|
// Http5xxHandler 5xx handler
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 14:55 2024/5/31
|
||||||
|
type Http5xxHandler func(req *Request, rep *Response)
|
||||||
|
|
||||||
|
// HttpBusinessErrorHandler 接口请求业务错误
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:04 2024/6/1
|
||||||
|
type HttpBusinessErrorHandler func(req *Request, rep *Response)
|
||||||
|
|
||||||
|
// RequestSendErrorHandler 请求发送失败的处理逻辑
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:23 2024/6/1
|
||||||
|
type RequestSendErrorHandler func(req *Request)
|
||||||
|
|
||||||
|
// RequestFinishHandler 请求最终完成事件, 不区分成功 OR 失败
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 18:34 2024/6/1
|
||||||
|
type RequestFinishHandler func(req *Request, rep *Response)
|
56
httpclient/define/request.go
Normal file
56
httpclient/define/request.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Package define ...
|
||||||
|
//
|
||||||
|
// Description : define ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-05-24 17:09
|
||||||
|
package define
|
||||||
|
|
||||||
|
// Request 请求配置
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:10 2024/5/24
|
||||||
|
type Request struct {
|
||||||
|
PathParam map[string]string `json:"path_param"` // 替换url中的占位符
|
||||||
|
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描述字段
|
||||||
|
DataReceiver interface{} `json:"-"` // 响应data部分数据解析
|
||||||
|
SuccessCodeList []string `json:"success_code_list"` // 哪些业务状态码视为成功
|
||||||
|
ConnectTimeout int64 `json:"connect_timeout"` // 连接超时时间: ms
|
||||||
|
ReadTimeout int64 `json:"read_timeout"` // 读取超时时间
|
||||||
|
RetryRule *RequestRetryRule `json:"retry_rule"` // 重试规则
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestRetryRule 重试规则
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 12:26 2024/5/31
|
||||||
|
type RequestRetryRule struct {
|
||||||
|
RetryCount int `json:"retry_count"` // 重试次数
|
||||||
|
RetryTimeInterval int64 `json:"retry_time_interval"` // 重试的时间间隔 1 - 10 之间, 单位毫秒
|
||||||
|
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"} // 默认成功业务状态码
|
||||||
|
)
|
70
httpclient/define/response.go
Normal file
70
httpclient/define/response.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Package define ...
|
||||||
|
//
|
||||||
|
// Description : define ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-05-31 12:34
|
||||||
|
package define
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Response 响应的数据结构定义
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 12:34 2024/5/31
|
||||||
|
type Response struct {
|
||||||
|
Header map[string]string `json:"header"` // 响应header
|
||||||
|
Cookie map[string]string `json:"cookie"` // 响应cookie
|
||||||
|
Data string `json:"data"` // 响应body
|
||||||
|
Code string `json:"code"` // 业务状态码
|
||||||
|
Message string `json:"message"` // 业务状态码描述
|
||||||
|
Body map[string]any `json:"body"` // 响应数据
|
||||||
|
ExtendData map[string]string `json:"extend_data"` // 除去 code / message / data 之外的其他数据
|
||||||
|
HttpCode int `json:"http_code"` // http状态码
|
||||||
|
HttpCodeStatus string `json:"http_code_status"` // http状态码描述
|
||||||
|
ResponseDataRule map[string]any `json:"response_data_rule"` // 返回数据的验证规则
|
||||||
|
Seq int `json:"seq"` // 第几次请求
|
||||||
|
RequestStartTime int64 `json:"request_start_time"` // 请求开始时间 : ms
|
||||||
|
RequestFinishTime int64 `json:"request_finish_time"` // 请求完成时间 : ms
|
||||||
|
UsedTime int64 `json:"used_time"` // 请求耗时 : ms
|
||||||
|
RestyResponse *resty.Response `json:"-"` // 请求返回
|
||||||
|
IsSuccess bool `json:"is_success"` // 是否请求成功
|
||||||
|
RequestCount int `json:"request_count"` // 请求次数
|
||||||
|
FailInfo *ResponseFailInfo `json:"fail_info"` // 请求失败信息记录
|
||||||
|
CacheInfo *ResponseCacheInfo `json:"cache_info"` // 缓存信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseFailInfo 失败信息
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:48 2024/6/1
|
||||||
|
type ResponseFailInfo struct {
|
||||||
|
Type string `json:"type"` // 失败类型
|
||||||
|
Message string `json:"message"` // 失败信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseCacheInfo 缓存信息
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 22:26 2024/6/14
|
||||||
|
type ResponseCacheInfo struct {
|
||||||
|
IsCache bool `json:"is_cache"` // 是否命中缓存
|
||||||
|
SetCache bool `json:"set_cache"` // 是否设置缓存
|
||||||
|
CacheKey string `json:"cache_key"` // 缓存key
|
||||||
|
CacheValue string `json:"cache_value"` // 缓存值
|
||||||
|
CacheEnable bool `json:"cache_enable"` // 是否允许缓存
|
||||||
|
CacheError error `json:"-"` // 缓存是否异常
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RequestFailTypeSend = "SEND_REQUEST_FAIL" // 发送请求即失败, 问题出现在客户端
|
||||||
|
RequestFailTypeClientError = "CLIENT_REQUEST_ERROR" // 请求失败, 原因出在客户端, 对应http code 4xx
|
||||||
|
RequestFailTypeServerError = "SERVER_DEAL_ERROR" // 服务端处理失败, 对应 http code 5xx
|
||||||
|
RequestFailTypeBusinessError = "SERVICE_BUSINESS_ERROR" // 返回状态码为200, 但是业务状态码非成功
|
||||||
|
)
|
102
httpclient/resty.go
Normal file
102
httpclient/resty.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// Package httpclient ...
|
||||||
|
//
|
||||||
|
// Description : httpclient ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-05-31 14:59
|
||||||
|
package httpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"git.zhangdeman.cn/gateway/httpclient/define"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/consts"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRestyClient 获取resty client
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:00 2024/5/31
|
||||||
|
func NewRestyClient(reqConfig *define.Request) (*resty.Client, *resty.Request) {
|
||||||
|
client := resty.New()
|
||||||
|
request := client.R()
|
||||||
|
if nil == reqConfig {
|
||||||
|
return client, request
|
||||||
|
}
|
||||||
|
formatHeader(reqConfig)
|
||||||
|
client.SetAllowGetMethodPayload(true) // 配置 GET 请求允许带 Body
|
||||||
|
client.SetJSONMarshaler(json.Marshal) // 序列化方法
|
||||||
|
client.SetJSONEscapeHTML(true) // 处理html实体字符
|
||||||
|
client.SetJSONUnmarshaler(serialize.JSON.UnmarshalWithNumber) // 反序列化方法
|
||||||
|
|
||||||
|
request.SetPathParams(reqConfig.PathParam) // 设置path中的参数
|
||||||
|
request.SetQueryParams(reqConfig.Query) // 设置query参数
|
||||||
|
request.SetHeaders(reqConfig.Header) // 设置header
|
||||||
|
request.URL = reqConfig.FullUrl // 请求接口
|
||||||
|
for pathParamName, pathParamValue := range reqConfig.PathParam {
|
||||||
|
if len(pathParamValue) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
reqConfig.FullUrl = strings.ReplaceAll(reqConfig.FullUrl, "{#"+pathParamName+"#}", pathParamValue)
|
||||||
|
}
|
||||||
|
request.Method = reqConfig.Method // 请求方法
|
||||||
|
cookieList := make([]*http.Cookie, 0)
|
||||||
|
for cookieName, cookieValue := range reqConfig.Cookie {
|
||||||
|
cookieList = append(cookieList, &http.Cookie{
|
||||||
|
Name: cookieName,
|
||||||
|
Value: cookieValue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
request.SetCookies(cookieList) // 设置cookie
|
||||||
|
setRestyBody(reqConfig, request) // 设置请求Body
|
||||||
|
return client, request
|
||||||
|
}
|
||||||
|
|
||||||
|
// setRestyBody 设置请求BODY
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:18 2024/5/31
|
||||||
|
func setRestyBody(reqConfig *define.Request, request *resty.Request) {
|
||||||
|
if nil == reqConfig.Body || len(reqConfig.Body) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.Contains(strings.ToLower(reqConfig.ContentType), consts.MimeTypeJson) {
|
||||||
|
request.SetBody(reqConfig.Body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.Contains(strings.ToLower(reqConfig.ContentType), consts.MimeTypeXWWWFormUrlencoded) {
|
||||||
|
bodyStr := serialize.JSON.MarshalForStringIgnoreError(reqConfig.Body)
|
||||||
|
bodyData := map[string]string{}
|
||||||
|
jsonObj := gjson.Parse(bodyStr)
|
||||||
|
jsonObj.ForEach(func(key, value gjson.Result) bool {
|
||||||
|
bodyData[key.String()] = value.String()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
request.SetFormData(bodyData)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
131
httpclient/validate/request_config.go
Normal file
131
httpclient/validate/request_config.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// 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 nil == reqConfig.RetryRule {
|
||||||
|
reqConfig.RetryRule = &define.RequestRetryRule{
|
||||||
|
RetryCount: 0,
|
||||||
|
RetryTimeInterval: 5,
|
||||||
|
RetryHttpCodeList: make([]int64, 0),
|
||||||
|
RetryBusinessCodeList: make([]string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nil == reqConfig.Header {
|
||||||
|
reqConfig.Header = map[string]string{}
|
||||||
|
}
|
||||||
|
if len(reqConfig.ContentType) > 0 {
|
||||||
|
reqConfig.Header["Content-Type"] = reqConfig.ContentType
|
||||||
|
}
|
||||||
|
if nil == reqConfig.Cookie {
|
||||||
|
reqConfig.Cookie = map[string]string{}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user