在Spring Cloud中有非常方便好用的WebClient,下面的例子,实现了类似的go版本WebClient(基于Consul的服务注册发现),并且支持处理自定义异常信息
WebClient
package core
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/consul/api"
)
type WebClient struct {
HttpClient *http.Client
ConsulCient *api.Client
}
func (m *WebClient) Get(service string, path string, urlValues url.Values) (int, []byte, error) {
return m.Do("GET", "application/json;charset=UTF-8", service, path, urlValues, nil)
}
func (m *WebClient) Post(service string, path string, bodyValue interface{}) (int, []byte, error) {
return m.Do("POST", "application/json;charset=UTF-8", service, path, nil, bodyValue)
}
func (m *WebClient) Put(service string, path string, urlValues url.Values, bodyValue interface{}) (int, []byte, error) {
return m.Do("PUT", "application/json;charset=UTF-8", service, path, urlValues, bodyValue)
}
func (m *WebClient) Delete(service string, path string, urlValues url.Values, bodyValue interface{}) (int, []byte, error) {
return m.Do("DELETE", "application/json;charset=UTF-8", service, path, urlValues, bodyValue)
}
func (m *WebClient) Do(method string, contentType string, service string, path string, urlValues url.Values, bodyValue interface{}) (int, []byte, error) {
serviceEntry, err := m.serviceEntry(service)
if err != nil {
return 0, nil, err
}
bodyValueBytes, err1 := json.Marshal(bodyValue)
if err1 != nil {
return 0, nil, err1
}
if req, err := http.NewRequest(method, m.url(serviceEntry, path, urlValues), bytes.NewReader(bodyValueBytes)); err != nil {
return 0, nil, err
} else {
return m.doRequest(req, contentType)
}
}
func (m *WebClient) serviceEntry(service string) (*api.ServiceEntry, error) {
serviceEntries, _, err := m.ConsulCient.Health().Service(service, "", true, nil)
if err != nil {
return nil, err
}
if len(serviceEntries) == 0 {
return nil, errors.New("no service to connect")
}
return serviceEntries[rand.Intn(len(serviceEntries))], nil
}
func (m *WebClient) url(serviceEntry *api.ServiceEntry, path string, urlValues url.Values) string {
scheme := "http"
if meta, ok := serviceEntry.Service.Meta["secure"]; ok {
if secure, err := strconv.ParseBool(meta); err == nil && secure {
scheme = "https"
}
}
url := &url.URL{
Scheme: scheme,
Host: fmt.Sprintf("%s:%d", serviceEntry.Service.Address, serviceEntry.Service.Port),
Path: path,
}
sb := new(strings.Builder)
sb.WriteString(url.String())
if len(urlValues) > 0 {
sb.WriteString("?")
sb.WriteString(urlValues.Encode())
}
return sb.String()
}
func (m *WebClient) doRequest(req *http.Request, contentType string) (int, []byte, error) {
req.Header.Set("Content-Type", contentType)
if res, err := m.HttpClient.Do(req); err != nil {
return res.StatusCode, nil, err
} else {
if resBytes, err := io.ReadAll(res.Body); err != nil {
return res.StatusCode, nil, err
} else {
return res.StatusCode, resBytes, nil
}
}
}
WebClient Test
package core
import (
"encoding/json"
"fmt"
"net/http"
"testing"
"github.com/hashicorp/consul/api"
)
func Test_Get(t *testing.T) {
config := api.DefaultConfig()
config.Address = "http://localhost:8500"
consulClient, _ := api.NewClient(config)
httpClient := &http.Client{}
webClient := &WebClient{HttpClient: httpClient, ConsulCient: consulClient}
if statusCode, resBytes, err := webClient.Get("user-service", "/users/1", nil); err != nil {
fmt.Println(err)
} else if statusCode != 200 {
businessErr := new(BusinessException)
json.Unmarshal(resBytes, &businessErr)
fmt.Println(businessErr)
} else {
result := make(map[string]interface{})
json.Unmarshal(resBytes, &result)
fmt.Print(result)
}
}
func Test_Post(t *testing.T) {
config := api.DefaultConfig()
config.Address = "http://localhost:8500"
consulClient, _ := api.NewClient(config)
httpClient := &http.Client{}
webClient := &WebClient{HttpClient: httpClient, ConsulCient: consulClient}
searchParam := &PageParam{PageIndex: 1, PageSize: 50}
if statusCode, resBytes, err := webClient.Post("app-service", "/page-configs/search/page", searchParam); err != nil {
fmt.Print(err)
} else if statusCode != 200 {
businessErr := new(BusinessException)
json.Unmarshal(resBytes, &businessErr)
fmt.Println(businessErr)
} else {
result := make(map[string]interface{})
json.Unmarshal(resBytes, &result)
fmt.Println(result)
}
}
func Test_BussinessException(t *testing.T) {
config := api.DefaultConfig()
config.Address = "http://localhost:8500"
consulClient, _ := api.NewClient(config)
httpClient := &http.Client{}
webClient := &WebClient{HttpClient: httpClient, ConsulCient: consulClient}
if statusCode, resBytes, err := webClient.Post("user-service", "/users", nil); err != nil {
fmt.Println(err)
} else if statusCode != 200 {
businessErr := new(BusinessException)
json.Unmarshal(resBytes, &businessErr)
fmt.Println(businessErr)
} else {
result := make(map[string]interface{})
json.Unmarshal(resBytes, &result)
fmt.Print(result)
}
}
BusinessException
package core
type BusinessException struct {
Status int `json:"status"`
Code string `json:"code"`
Message string `json:"message"`
Trace string `json:"trace"`
}
func (e *BusinessException) Error() string {
return e.Trace
}
网友评论