network/httpclient/request.go

351 lines
11 KiB
Go

// Package httpclient ...
//
// Author: go_developer@163.com<白茶清欢>
//
// Description:
//
// File: request.go
//
// Version: 1.0.0
//
// Date: 2022/05/01 21:25:03
package httpclient
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"git.zhangdeman.cn/zhangdeman/exception"
"github.com/tidwall/gjson"
"github.com/ddliu/go-httpclient"
)
// Request 发送请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:26:02
func Request(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = strings.ToUpper(apiConfig.Method)
switch apiConfig.Method {
case http.MethodGet:
return GET(apiConfig, header, param)
case http.MethodPost:
return POST(apiConfig, header, param)
case http.MethodPut:
return PUT(apiConfig, header, param)
case http.MethodDelete:
return DELETE(apiConfig, header, param)
case http.MethodConnect:
return Connect(apiConfig, header, param)
case http.MethodOptions:
return OPTION(apiConfig, header, param)
case http.MethodTrace:
return Trace(apiConfig, header, param)
case http.MethodPatch:
return Patch(apiConfig, header, param)
default:
return &ApiResponse{
RequestConfig: apiConfig,
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(),
}
}
}
// GET ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date: 2022/05/01 21:29:09
func GET(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodGet
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// POST post请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:31:36
func POST(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodPost
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// PUT put请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:31:52
func PUT(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodPut
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// DELETE delete请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:32:08
func DELETE(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodDelete
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// OPTION option请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:32:18
func OPTION(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodOptions
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// Patch patch请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:36:12
func Patch(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodPatch
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// Trace trace请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:36:24
func Trace(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodTrace
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// Connect connect请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:36:39
func Connect(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) *ApiResponse {
apiConfig.Method = http.MethodConnect
buildRequestURLAndParam(apiConfig, header, param)
return send(apiConfig, header)
}
// getHttpClient 获取httpclient实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:27 2022/5/1
func getHttpClient(apiConfig *ApiRequestConfig, header map[string]string) *httpclient.HttpClient {
fullHeader := make(map[string]string)
for name, val := range apiConfig.CommonHeader {
fullHeader[name] = val
}
for name, val := range header {
fullHeader[name] = val
}
if len(apiConfig.ContentType) == 0 {
apiConfig.ContentType = ContentTypeDefault
}
fullHeader["content-type"] = apiConfig.ContentType
return httpclient.NewHttpClient().WithHeaders(fullHeader).
WithOption(httpclient.OPT_CONNECTTIMEOUT_MS, apiConfig.Timeout.Connect).
WithOption(httpclient.OPT_TIMEOUT_MS, apiConfig.Timeout.Read)
}
// buildRequestURLAndParam 构建完整请求URL与请求参数
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:55 2022/5/1
func buildRequestURLAndParam(apiConfig *ApiRequestConfig, header map[string]string, param map[string]interface{}) {
apiConfig.Method = strings.ToUpper(apiConfig.Method)
if apiConfig.Timeout.Connect == 0 {
apiConfig.Timeout.Connect = DefaultConnectTimeout
}
if apiConfig.Timeout.Read == 0 {
apiConfig.Timeout.Read = DefaultConnectTimeout
}
formatParam := make(map[string]interface{})
for paramName, paramValue := range param {
uriTemplate := "{" + paramName + "}"
if strings.Contains(apiConfig.URI, uriTemplate) {
apiConfig.URI = strings.ReplaceAll(apiConfig.URI, uriTemplate, fmt.Sprintf("%v", paramValue))
continue
}
formatParam[paramName] = paramValue
}
apiConfig.Parameter = formatParam
paramPair := make([]string, 0)
switch apiConfig.Method {
case http.MethodPost:
apiConfig.Body, _ = json.Marshal(formatParam)
default:
for paramName, paramValue := range formatParam {
paramPair = append(paramPair, fmt.Sprintf("%v=%v", paramName, paramValue))
}
if len(paramPair) > 0 {
apiConfig.URI = apiConfig.URI + "?" + strings.Join(paramPair, "&")
}
}
if strings.HasSuffix(apiConfig.Domain, "/") {
apiConfig.Domain = strings.TrimRight(apiConfig.Domain, "/")
}
if !strings.HasPrefix(apiConfig.URI, "/") {
apiConfig.URI = "/" + apiConfig.URI
}
apiConfig.FullURL = apiConfig.Domain + apiConfig.URI
}
// send 发送请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:53 2022/5/2
func send(apiConfig *ApiRequestConfig, header map[string]string) *ApiResponse {
var (
client *httpclient.HttpClient
err error
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,
Exception: nil,
StartRequestTime: time.Now().UnixNano(),
FinishRequestTime: 0,
Code: "",
Message: "",
Data: "",
}
defer func() {
response.FinishRequestTime = time.Now().UnixNano()
}()
client = getHttpClient(apiConfig, header)
if response.Response, err = client.Do(apiConfig.Method, apiConfig.FullURL, nil, bytes.NewReader(apiConfig.Body)); nil != err {
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 = exception.New(ReadResponseBodyError, response.Response.StatusCode, map[string]string{"real_reason": err.Error()}, "response body read fail : "+err.Error())
return response
}
// 判断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 {
if strings.ToLower(response.RequestConfig.ResponseCodeField) == "http_code" {
response.Code = responseHttpCode
} else {
response.Code = response.Response.Header.Get(response.RequestConfig.ResponseCodeField)
}
} else {
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))
return response
}
// 提取响应文案
response.Message = gjson.GetBytes(responseByte, response.RequestConfig.ResponseMessageField).String()
// 判断响应状态码是否成功
isBusinessCodeSuccess := false
for _, itemSuccessCode := range response.RequestConfig.SuccessCodeList {
if itemSuccessCode == response.Code {
isBusinessCodeSuccess = true
break
}
}
if len(response.Message) == 0 {
if isBusinessCodeSuccess {
response.Message = DefaultSuccessMessage
} else {
response.Message = DefaultFailMessage
}
}
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
}