2023-08-10 14:29:19 +08:00
// Package httpclient ...
2022-05-01 21:49:17 +08:00
//
// Author: go_developer@163.com<白茶清欢>
//
// Description:
//
// File: request.go
//
// Version: 1.0.0
//
// Date: 2022/05/01 21:25:03
2023-08-10 14:29:19 +08:00
package httpclient
2022-05-01 21:49:17 +08:00
import (
2022-05-02 15:17:21 +08:00
"bytes"
2022-05-01 22:21:49 +08:00
"encoding/json"
"fmt"
2023-08-26 20:31:21 +08:00
"io"
2022-05-01 21:49:17 +08:00
"net/http"
"strings"
2022-05-02 15:17:21 +08:00
"time"
2022-05-01 22:38:31 +08:00
2023-08-26 21:31:09 +08:00
"git.zhangdeman.cn/zhangdeman/exception"
2023-08-26 20:31:21 +08:00
2023-08-26 21:31:09 +08:00
"github.com/tidwall/gjson"
2023-08-26 20:31:21 +08:00
2022-05-01 22:38:31 +08:00
"github.com/ddliu/go-httpclient"
2022-05-01 21:49:17 +08:00
)
// Request 发送请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:26:02
2022-05-01 22:38:31 +08:00
func Request ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-01 21:49:17 +08:00
apiConfig . Method = strings . ToUpper ( apiConfig . Method )
switch apiConfig . Method {
case http . MethodGet :
2022-05-01 22:38:31 +08:00
return GET ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
case http . MethodPost :
2022-05-01 22:38:31 +08:00
return POST ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
case http . MethodPut :
2022-05-01 22:38:31 +08:00
return PUT ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
case http . MethodDelete :
2022-05-01 22:38:31 +08:00
return DELETE ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
case http . MethodConnect :
2022-05-01 22:38:31 +08:00
return Connect ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
case http . MethodOptions :
2022-05-01 22:38:31 +08:00
return OPTION ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
case http . MethodTrace :
2022-05-01 22:38:31 +08:00
return Trace ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
case http . MethodPatch :
2022-05-01 22:38:31 +08:00
return Patch ( apiConfig , header , param )
2022-05-01 21:49:17 +08:00
default :
return & ApiResponse {
2022-05-02 15:17:21 +08:00
RequestConfig : apiConfig ,
2023-08-26 21:31:09 +08:00
Exception : exception . New ( RequestMethodNotSupport , 0 , map [ string ] string { "method" : apiConfig . Method } , apiConfig . Method + " : request method is not support" ) ,
2022-05-02 15:17:21 +08:00
Response : nil ,
StartRequestTime : time . Now ( ) . UnixNano ( ) ,
FinishRequestTime : time . Now ( ) . UnixNano ( ) ,
2022-05-01 21:49:17 +08:00
}
}
}
// GET ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date: 2022/05/01 21:29:09
2022-05-01 22:38:31 +08:00
func GET ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodGet
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
// POST post请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:31:36
2022-05-01 22:38:31 +08:00
func POST ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodPost
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
// PUT put请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:31:52
2022-05-01 22:38:31 +08:00
func PUT ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodPut
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
// DELETE delete请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:32:08
2022-05-01 22:38:31 +08:00
func DELETE ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodDelete
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
// OPTION option请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:32:18
2022-05-01 22:38:31 +08:00
func OPTION ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodOptions
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
// Patch patch请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:36:12
2022-05-01 22:38:31 +08:00
func Patch ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodPatch
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
// Trace trace请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:36:24
2022-05-01 22:38:31 +08:00
func Trace ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodTrace
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
// Connect connect请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/05/01 21:36:39
2022-05-01 22:38:31 +08:00
func Connect ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) * ApiResponse {
2022-05-02 15:17:21 +08:00
apiConfig . Method = http . MethodConnect
buildRequestURLAndParam ( apiConfig , header , param )
return send ( apiConfig , header )
2022-05-01 21:49:17 +08:00
}
2022-05-01 22:21:49 +08:00
2022-05-01 22:38:31 +08:00
// getHttpClient 获取httpclient实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:27 2022/5/1
2022-05-02 15:17:21 +08:00
func getHttpClient ( apiConfig * ApiRequestConfig , header map [ string ] string ) * httpclient . HttpClient {
2022-05-01 22:38:31 +08:00
fullHeader := make ( map [ string ] string )
for name , val := range apiConfig . CommonHeader {
fullHeader [ name ] = val
}
for name , val := range header {
fullHeader [ name ] = val
}
2022-05-02 15:30:55 +08:00
if len ( apiConfig . ContentType ) == 0 {
apiConfig . ContentType = ContentTypeDefault
}
fullHeader [ "content-type" ] = apiConfig . ContentType
return httpclient . NewHttpClient ( ) . WithHeaders ( fullHeader ) .
2023-03-10 16:29:19 +08:00
WithOption ( httpclient . OPT_CONNECTTIMEOUT_MS , apiConfig . Timeout . Connect ) .
WithOption ( httpclient . OPT_TIMEOUT_MS , apiConfig . Timeout . Read )
2022-05-01 22:38:31 +08:00
}
2022-05-01 22:21:49 +08:00
// buildRequestURLAndParam 构建完整请求URL与请求参数
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:55 2022/5/1
2022-05-02 15:17:21 +08:00
func buildRequestURLAndParam ( apiConfig * ApiRequestConfig , header map [ string ] string , param map [ string ] interface { } ) {
2022-05-01 22:21:49 +08:00
apiConfig . Method = strings . ToUpper ( apiConfig . Method )
2022-05-02 15:30:55 +08:00
if apiConfig . Timeout . Connect == 0 {
apiConfig . Timeout . Connect = DefaultConnectTimeout
}
if apiConfig . Timeout . Read == 0 {
apiConfig . Timeout . Read = DefaultConnectTimeout
}
2022-05-01 22:21:49 +08:00
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 , "&" )
}
}
2023-03-10 16:29:19 +08:00
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
2022-05-02 15:17:21 +08:00
}
// send 发送请求
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:53 2022/5/2
func send ( apiConfig * ApiRequestConfig , header map [ string ] string ) * ApiResponse {
var (
2023-08-26 20:31:21 +08:00
client * httpclient . HttpClient
err error
responseByte [ ] byte
2022-05-02 15:17:21 +08:00
)
2022-05-01 22:21:49 +08:00
2023-08-26 21:31:09 +08:00
// 填充默认配置
2023-08-26 20:31:21 +08:00
if len ( apiConfig . ResponseCodeFieldLocation ) == 0 {
apiConfig . ResponseCodeFieldLocation = ResponseCodeFieldLocationDefault
}
2023-08-26 21:31:09 +08:00
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
}
2022-05-02 15:17:21 +08:00
response := & ApiResponse {
RequestConfig : apiConfig ,
Response : nil ,
Exception : nil ,
StartRequestTime : time . Now ( ) . UnixNano ( ) ,
FinishRequestTime : 0 ,
2023-08-26 20:31:21 +08:00
Code : "" ,
Message : "" ,
2023-08-26 21:31:09 +08:00
Data : "" ,
2022-05-02 15:17:21 +08:00
}
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 {
2023-08-26 21:31:09 +08:00
response . Exception = exception . New ( SendRequestError , 0 , map [ string ] string { "real_reason" : err . Error ( ) } , "http request send fail : " + err . Error ( ) )
2023-08-26 20:31:21 +08:00
return response
}
if responseByte , err = io . ReadAll ( response . Response . Body ) ; nil != err {
2023-08-26 21:31:09 +08:00
response . Exception = exception . New ( ReadResponseBodyError , response . Response . StatusCode , map [ string ] string { "real_reason" : err . Error ( ) } , "response body read fail : " + err . Error ( ) )
2023-08-26 20:31:21 +08:00
return response
}
2023-08-26 21:31:09 +08:00
// 判断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
2023-08-26 20:31:21 +08:00
}
// 提取响应错误码
if response . RequestConfig . ResponseCodeFieldLocation == ResponseCodeFieldLocationHeader {
response . Code = response . Response . Header . Get ( response . RequestConfig . ResponseCodeField )
} else {
2023-08-26 21:31:09 +08:00
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 ) )
2022-05-02 15:17:21 +08:00
}
2023-08-26 20:31:21 +08:00
// 提取响应文案
response . Message = gjson . GetBytes ( responseByte , response . RequestConfig . ResponseMessageField ) . String ( )
2023-08-26 21:31:09 +08:00
// 判断响应状态码是否成功
isBusinessCodeSuccess := false
for _ , itemSuccessCode := range response . RequestConfig . SuccessHttpCodeList {
if itemSuccessCode == response . Code {
isBusinessCodeSuccess = true
break
}
}
2023-08-26 22:13:25 +08:00
if len ( response . Message ) == 0 {
if isBusinessCodeSuccess {
response . Message = DefaultSuccessMessage
} else {
response . Message = DefaultFailMessage
}
}
2023-08-26 21:31:09 +08:00
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 ( )
}
2022-05-02 15:17:21 +08:00
return response
2022-05-01 22:21:49 +08:00
}