174 lines
5.1 KiB
Go
174 lines
5.1 KiB
Go
// Package httpclient ...
|
|
//
|
|
// Description : httpclient ...
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 2024-05-31 14:59
|
|
package httpclient
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"git.zhangdeman.cn/zhangdeman/consts"
|
|
"git.zhangdeman.cn/zhangdeman/network/httpclient/define"
|
|
requestBoodyWriter "git.zhangdeman.cn/zhangdeman/network/httpclient/implement/request"
|
|
"git.zhangdeman.cn/zhangdeman/wrapper"
|
|
"net/http"
|
|
"net/textproto"
|
|
"resty.dev/v3"
|
|
"strings"
|
|
)
|
|
|
|
// 初始化请求配置, 包括:
|
|
// 1. 初始化空指针
|
|
// 2. 合并static参数
|
|
// 3. 使用 textproto.CanonicalMIMEHeaderKey 统一格式化header key
|
|
func initRequestConfig(reqConfig *define.Request) {
|
|
if nil == reqConfig.Header {
|
|
reqConfig.Header = map[string]any{}
|
|
}
|
|
if nil == reqConfig.Body {
|
|
reqConfig.Body = map[string]any{}
|
|
}
|
|
if nil == reqConfig.Cookie {
|
|
reqConfig.Cookie = map[string]any{}
|
|
}
|
|
if reqConfig.Query == nil {
|
|
reqConfig.Query = map[string]any{}
|
|
}
|
|
if reqConfig.PathParam == nil {
|
|
reqConfig.PathParam = map[string]string{}
|
|
}
|
|
|
|
// 合并静态参数, 传入参数与静态参数存在相同参数, 以静态参数为准
|
|
for loc, paramTable := range reqConfig.Static {
|
|
switch strings.ToUpper(loc) {
|
|
case consts.RequestDataLocationBody.String():
|
|
for name, val := range paramTable {
|
|
reqConfig.Body[name] = val
|
|
}
|
|
case consts.RequestDataLocationQuery.String():
|
|
for name, val := range paramTable {
|
|
reqConfig.Query[name] = val
|
|
}
|
|
case consts.RequestDataLocationCookie.String():
|
|
for name, val := range paramTable {
|
|
reqConfig.Cookie[name] = val
|
|
}
|
|
case consts.RequestDataLocationHeader.String():
|
|
for name, val := range paramTable {
|
|
reqConfig.Header[name] = val
|
|
}
|
|
case consts.RequestDataLocationUriPath.String():
|
|
for name, val := range paramTable {
|
|
reqConfig.PathParam[name] = fmt.Sprintf("%v", val)
|
|
}
|
|
}
|
|
}
|
|
|
|
formatHeader(reqConfig)
|
|
}
|
|
|
|
// NewRestyClient 获取resty client
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 15:00 2024/5/31
|
|
func NewRestyClient(reqConfig *define.Request, reqOption *RequestOption) (*resty.Client, *resty.Request, error) {
|
|
client := resty.New()
|
|
request := client.R()
|
|
if nil == reqConfig {
|
|
return nil, nil, errors.New("request config is nil")
|
|
}
|
|
// 限流处理, 增加限流中间件
|
|
client.AddRequestMiddleware(func(client *resty.Client, request *resty.Request) error {
|
|
if nil != reqOption && nil != reqOption.RateLimiter {
|
|
// 未配置流控
|
|
return nil
|
|
}
|
|
if !reqOption.RateLimiter.Allow() {
|
|
// 命中流控
|
|
return define.ErrRateLimitExceeded
|
|
}
|
|
return nil
|
|
})
|
|
|
|
initRequestConfig(reqConfig) // 初始化 + 格式化配置
|
|
client.SetAllowMethodGetPayload(true) // 配置 GET 请求允许带 Body
|
|
client.SetAllowMethodDeletePayload(true) // 配置 DELETE 请求允许带 Body
|
|
client.SetJSONEscapeHTML(true) // 处理html实体字符
|
|
|
|
request.SetPathParams(reqConfig.PathParam) // 设置path中的参数
|
|
query := map[string]string{}
|
|
for queryName, queryValue := range reqConfig.Query {
|
|
if nil == queryValue {
|
|
continue
|
|
}
|
|
query[queryName] = wrapper.AnyDataType(queryValue).ToString().Value()
|
|
}
|
|
request.SetQueryParams(query) // 设置query参数
|
|
header := map[string]string{}
|
|
for headerName, headerValue := range reqConfig.Header {
|
|
if nil == headerValue {
|
|
continue
|
|
}
|
|
header[headerName] = wrapper.AnyDataType(headerValue).ToString().Value()
|
|
}
|
|
request.SetHeaders(header) // 设置header
|
|
request.URL = reqConfig.FullUrl // 请求接口
|
|
// 设置url参数, url path格式需要是 /user/{user_id}/detail格式, {user_id} 会自动替换
|
|
request.SetPathParams(reqConfig.PathParam)
|
|
request.Method = reqConfig.Method // 请求方法
|
|
cookieList := make([]*http.Cookie, 0)
|
|
for cookieName, cookieValue := range reqConfig.Cookie {
|
|
if nil == cookieValue {
|
|
continue
|
|
}
|
|
cookieList = append(cookieList, &http.Cookie{
|
|
Name: cookieName,
|
|
Value: wrapper.AnyDataType(cookieValue).ToString().Value(),
|
|
})
|
|
}
|
|
request.SetCookies(cookieList) // 设置cookie
|
|
if err := setRestyBody(reqConfig, reqOption, request); nil != err {
|
|
return nil, nil, err
|
|
}
|
|
return client, request, nil
|
|
}
|
|
|
|
// setRestyBody 设置请求BODY
|
|
//
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
//
|
|
// Date : 17:18 2024/5/31
|
|
func setRestyBody(reqConfig *define.Request, requestOption *RequestOption, request *resty.Request) error {
|
|
if nil == reqConfig.Body {
|
|
return nil
|
|
}
|
|
if nil != requestOption && nil != requestOption.RequestBodyWrite {
|
|
// 外部传入的实现
|
|
requestOption.RequestBodyWrite.Write(request, reqConfig.Body)
|
|
return nil
|
|
}
|
|
// 外部没传入, 使用内置实现, 内置默认实现支持: xml/json/form, 如需其他, 自行扩展
|
|
return requestBoodyWriter.WriteBody(reqConfig.ContentType, request, reqConfig.Body)
|
|
}
|
|
|
|
// 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]any)
|
|
for headerName, headerVal := range requestConfig.Header {
|
|
// 规范化处理 header 名称
|
|
formatHeaderData[textproto.CanonicalMIMEHeaderKey(headerName)] = headerVal
|
|
}
|
|
requestConfig.Header = formatHeaderData
|
|
}
|