// 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 { 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)) } // 提取响应文案 response.Message = gjson.GetBytes(responseByte, response.RequestConfig.ResponseMessageField).String() if len(response.Message) == 0 { response.Message = DefaultMessage } // 判断响应状态码是否成功 isBusinessCodeSuccess := false for _, itemSuccessCode := range response.RequestConfig.SuccessHttpCodeList { if itemSuccessCode == response.Code { isBusinessCodeSuccess = true break } } 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 }