network/httpclient/resty.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
}