Repository: zeromicro/go-zero
Pull request #1643 (2022-03-14 11:59:28)

refactor: httpc package for easy to use


Patch:
diff --git a/rest/httpc/requests.go b/rest/httpc/requests.go
index a01aaaf4..0f933654 100644
--- a/rest/httpc/requests.go
+++ b/rest/httpc/requests.go
@@ -3,72 +3,19 @@ package httpc
 import (
 	"io"
 	"net/http"
-
-	"github.com/zeromicro/go-zero/core/breaker"
-	"github.com/zeromicro/go-zero/core/logx"
-	"github.com/zeromicro/go-zero/rest/httpc/internal"
 )
 
-var interceptors = []internal.Interceptor{
-	internal.LogInterceptor,
-}
-
-type Option func(cli *http.Client)
-
 // Do sends an HTTP request to the service assocated with the given key.
-func Do(key string, r *http.Request, opts ...Option) (resp *http.Response, err error) {
-	var respHandlers []internal.ResponseHandler
-	for _, interceptor := range interceptors {
-		var h internal.ResponseHandler
-		r, h = interceptor(r)
-		respHandlers = append(respHandlers, h)
-	}
-
-	resp, err = doRequest(key, r, opts...)
-	if err != nil {
-		logx.Errorf("[HTTP] %s %s/%s - %v", r.Method, r.Host, r.RequestURI, err)
-		return
-	}
-
-	for i := len(respHandlers) - 1; i >= 0; i-- {
-		respHandlers[i](resp)
-	}
-
-	return
+func Do(key string, r *http.Request, opts ...Option) (*http.Response, error) {
+	return NewService(key, opts...).Do(r)
 }
 
 // Get sends an HTTP GET request to the service assocated with the given key.
 func Get(key, url string, opts ...Option) (*http.Response, error) {
-	r, err := http.NewRequest(http.MethodGet, url, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return Do(key, r, opts...)
+	return NewService(key, opts...).Get(url)
 }
 
 // Post sends an HTTP POST request to the service assocated with the given key.
 func Post(key, url, contentType string, body io.Reader, opts ...Option) (*http.Response, error) {
-	r, err := http.NewRequest(http.MethodPost, url, body)
-	if err != nil {
-		return nil, err
-	}
-
-	return Do(key, r, opts...)
-}
-
-func doRequest(key string, r *http.Request, opts ...Option) (resp *http.Response, err error) {
-	brk := breaker.GetBreaker(key)
-	err = brk.DoWithAcceptable(func() error {
-		var cli http.Client
-		for _, opt := range opts {
-			opt(&cli)
-		}
-		resp, err = cli.Do(r)
-		return err
-	}, func(err error) bool {
-		return err == nil && resp.StatusCode < http.StatusInternalServerError
-	})
-
-	return
+	return NewService(key, opts...).Post(url, contentType, body)
 }
diff --git a/rest/httpc/service.go b/rest/httpc/service.go
index eb917391..f03005e0 100644
--- a/rest/httpc/service.go
+++ b/rest/httpc/service.go
@@ -3,37 +3,110 @@ package httpc
 import (
 	"io"
 	"net/http"
+
+	"github.com/zeromicro/go-zero/core/breaker"
+	"github.com/zeromicro/go-zero/core/logx"
+	"github.com/zeromicro/go-zero/rest/httpc/internal"
 )
 
+// ContentType means Content-Type.
+const ContentType = "Content-Type"
+
+var interceptors = []internal.Interceptor{
+	internal.LogInterceptor,
+}
+
 type (
+	// Option is used to customize the *http.Client.
+	Option func(cli *http.Client)
+
+	// Service represents a remote HTTP service.
 	Service interface {
-		Do(r *http.Request, opts ...Option) (*http.Response, error)
-		Get(url string, opts ...Option) (*http.Response, error)
-		Post(url, contentType string, body io.Reader, opts ...Option) (*http.Response, error)
+		// Do sends an HTTP request to the service.
+		Do(r *http.Request) (*http.Response, error)
+		// Get sends an HTTP GET request to the service.
+		Get(url string) (*http.Response, error)
+		// Post sends an HTTP POST request to the service.
+		Post(url, contentType string, body io.Reader) (*http.Response, error)
 	}
 
 	namedService struct {
 		name string
-		opts []Option
+		cli  *http.Client
 	}
 )
 
+// NewService returns a remote service with the given name.
+// opts are used to customize the *http.Client.
 func NewService(name string, opts ...Option) Service {
+	var cli *http.Client
+
+	if len(opts) == 0 {
+		cli = http.DefaultClient
+	} else {
+		cli = &http.Client{}
+		for _, opt := range opts {
+			opt(cli)
+		}
+	}
+
 	return namedService{
 		name: name,
-		opts: opts,
+		cli:  cli,
 	}
 }
 
-func (s namedService) Do(r *http.Request, opts ...Option) (*http.Response, error) {
-	return Do(s.name, r, append(s.opts, opts...)...)
+// Do sends an HTTP request to the service.
+func (s namedService) Do(r *http.Request) (resp *http.Response, err error) {
+	var respHandlers []internal.ResponseHandler
+	for _, interceptor := range interceptors {
+		var h internal.ResponseHandler
+		r, h = interceptor(r)
+		respHandlers = append(respHandlers, h)
+	}
+
+	resp, err = s.doRequest(r)
+	if err != nil {
+		logx.Errorf("[HTTP] %s %s/%s - %v", r.Method, r.Host, r.RequestURI, err)
+		return
+	}
+
+	for i := len(respHandlers) - 1; i >= 0; i-- {
+		respHandlers[i](resp)
+	}
+
+	return
+}
+
+// Get sends an HTTP GET request to the service.
+func (s namedService) Get(url string) (*http.Response, error) {
+	r, err := http.NewRequest(http.MethodGet, url, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.Do(r)
 }
 
-func (s namedService) Get(url string, opts ...Option) (*http.Response, error) {
-	return Get(s.name, url, append(s.opts, opts...)...)
+// Post sends an HTTP POST request to the service.
+func (s namedService) Post(url, contentType string, body io.Reader) (*http.Response, error) {
+	r, err := http.NewRequest(http.MethodPost, url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	r.Header.Set(ContentType, contentType)
+	return s.Do(r)
 }
 
-func (s namedService) Post(url, contentType string, body io.Reader, opts ...Option) (
-	*http.Response, error) {
-	return Post(s.name, url, contentType, body, append(s.opts, opts...)...)
+func (s namedService) doRequest(r *http.Request) (resp *http.Response, err error) {
+	brk := breaker.GetBreaker(s.name)
+	err = brk.DoWithAcceptable(func() error {
+		resp, err = s.cli.Do(r)
+		return err
+	}, func(err error) bool {
+		return err == nil && resp.StatusCode < http.StatusInternalServerError
+	})
+
+	return
 }


Test patch:
diff --git a/rest/httpc/service_test.go b/rest/httpc/service_test.go
index 82e78bca..e01d4373 100644
--- a/rest/httpc/service_test.go
+++ b/rest/httpc/service_test.go
@@ -21,10 +21,10 @@ func TestNamedService_Do(t *testing.T) {
 func TestNamedService_Get(t *testing.T) {
 	svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	}))
-	service := NewService("foo")
-	resp, err := service.Get(svr.URL, func(cli *http.Client) {
+	service := NewService("foo", func(cli *http.Client) {
 		cli.Transport = http.DefaultTransport
 	})
+	resp, err := service.Get(svr.URL)
 	assert.Nil(t, err)
 	assert.Equal(t, http.StatusOK, resp.StatusCode)
 }