From 52fe072912bfea3ece4d1d34cc616a9f86444e89 Mon Sep 17 00:00:00 2001 From: xfali Date: Thu, 21 Jul 2022 21:54:51 +0800 Subject: [PATCH] refactor: v2.0 code feat: add UrlBuilder --- cookie/cookie.go | 4 +- cookie/cookie_test.go | 1 - default_converter.go | 16 +++-- param.go | 16 +++++ restutil/url_test.go | 130 ++++++++++++++++++++++++++++++++++++++++ restutil/urlutil.go | 112 +++++++++++++++++++++++++++++++++- test/restclient_test.go | 14 +++++ 7 files changed, 284 insertions(+), 9 deletions(-) diff --git a/cookie/cookie.go b/cookie/cookie.go index 3fcb49f..406663e 100644 --- a/cookie/cookie.go +++ b/cookie/cookie.go @@ -234,7 +234,7 @@ func (dm *defaultCache) Set(path string, cookie *http.Cookie) error { func checkCookiePath(path string, cookie *http.Cookie) { if cookie.Path == "" { - if path == "" || path== "/" { + if path == "" || path == "/" { cookie.Path = "/" } else { index := strings.LastIndex(path, "/") @@ -312,7 +312,7 @@ func (dm *defaultCache) Filter(request *http.Request, fc filter.FilterChain) (*h return resp, err } -func Filter(cache Cache) filter.Filter { +func Filter(cache Cache) filter.Filter { return func(request *http.Request, fc filter.FilterChain) (response *http.Response, e error) { path := request.URL.String() for _, v := range cache.Get(path) { diff --git a/cookie/cookie_test.go b/cookie/cookie_test.go index e5fe1ce..330cb52 100644 --- a/cookie/cookie_test.go +++ b/cookie/cookie_test.go @@ -19,7 +19,6 @@ package cookie import ( "context" "fmt" - "github.com/xfali/restclient/v2" "github.com/xfali/restclient/v2/request" "github.com/xfali/restclient/v2/restutil" "net/http" diff --git a/default_converter.go b/default_converter.go index 2f1fc44..84a0f5c 100644 --- a/default_converter.go +++ b/default_converter.go @@ -398,12 +398,18 @@ func (c *YamlConverter) CreateDecoder(r io.Reader) Decoder { return &YamlDecoder{d: yaml.NewDecoder(r)} } -func NewYamlConverter() *YamlConverter { +func NewYamlConverter(supportTypes ...string) *YamlConverter { + types := []MediaType{ + ParseMediaType(MediaTypeYaml), + BuildMediaType("application", "*yaml"), + } + for _, t := range supportTypes { + types = append(types, ParseMediaType(t)) + } return &YamlConverter{ - BaseConverter{[]MediaType{ - ParseMediaType(MediaTypeYaml), - BuildMediaType("application", "*yaml"), - }}, + BaseConverter{ + types, + }, } } diff --git a/param.go b/param.go index 560b677..160dc52 100644 --- a/param.go +++ b/param.go @@ -132,6 +132,18 @@ func (p *defaultParam) Header(header http.Header) *defaultParam { return p } +func (p *defaultParam) AddHeaders(key string, values ...string) *defaultParam { + for _, v := range values { + p.header.Add(key, v) + } + return p +} + +func (p *defaultParam) SetHeader(key string, value string) *defaultParam { + p.header.Set(key, value) + return p +} + func (p *defaultParam) Accept(accept string) *defaultParam { p.header.Add(restutil.HeaderAccept, accept) return p @@ -172,3 +184,7 @@ func (p *defaultParam) self(setter request.Setter) { func (p *defaultParam) Build() request.Opt { return p.self } + +func NewUrlBuilder(url string) *restutil.UrlBuilder { + return restutil.NewUrlBuilder(url) +} diff --git a/restutil/url_test.go b/restutil/url_test.go index 986235a..0df50e3 100644 --- a/restutil/url_test.go +++ b/restutil/url_test.go @@ -93,3 +93,133 @@ func TestPlaceholderUrl(t *testing.T) { t.Log(url) }) } + +func TestEncodeQuery(t *testing.T) { + t.Run("1", func(t *testing.T) { + url, err := Query("a", "1", "b", 2) + if err != nil { + t.Fatal(err) + } + if url != "a=1&b=2" { + t.Fatal(url) + } + + t.Log(url) + }) + + t.Run("2", func(t *testing.T) { + url := EncodeQuery(map[string]interface{}{ + "a": "1", + "b": 2, + }) + if url != "a=1&b=2" { + t.Fatal(url) + } + + t.Log(url) + }) +} + +func TestReplaceUrl(t *testing.T) { + t.Run("1", func(t *testing.T) { + url := ReplaceUrl("x", "", "", map[string]interface{}{ + "a": "1", + "b": 2, + }) + if url != "x" { + t.Fatal(url) + } + + t.Log(url) + }) + + t.Run("2", func(t *testing.T) { + url := ReplaceUrl("x/:a/tt?b=:b", ":", "", map[string]interface{}{ + "a": "1", + "b": 2, + }) + if url != "x/1/tt?b=2" { + t.Fatal(url) + } + + t.Log(url) + }) + + t.Run("3", func(t *testing.T) { + url := ReplaceUrl("x/:a/tt/:a?b=:b", ":", "", map[string]interface{}{ + "a": "1", + "b": 2, + }) + if url != "x/1/tt/1?b=2" { + t.Fatal(url) + } + + t.Log(url) + }) +} + +func TestUrlBuilder(t *testing.T) { + t.Run("1", func(t *testing.T) { + b := NewUrlBuilder("x") + b.PathVariable("a", "1") + b.PathVariable("b", 2) + url := b.Build() + if url != "x" { + t.Fatal(url) + } + + t.Log(url) + }) + + t.Run("2", func(t *testing.T) { + b := NewUrlBuilder("x/:a/tt?b=:b") + b.PathVariable("a", "1") + b.PathVariable("b", 2) + url := b.Build() + if url != "x/1/tt?b=2" { + t.Fatal(url) + } + + t.Log(url) + }) + + t.Run("3", func(t *testing.T) { + b := NewUrlBuilder("x/:a/tt/:a?b=:b") + b.PathVariable("a", "1") + b.PathVariable("b", 2) + url := b.Build() + if url != "x/1/tt/1?b=2" { + t.Fatal(url) + } + + t.Log(url) + }) + + t.Run("4", func(t *testing.T) { + b := NewUrlBuilder("x/:a/tt/:b") + b.PathVariable("a", "1") + b.PathVariable("b", 2) + b.QueryVariable("c", 100) + b.QueryVariable("d", 1.1) + url := b.Build() + if url != "x/1/tt/2?c=100&d=1.1" { + t.Fatal(url) + } + + t.Log(url) + }) + + t.Run("4", func(t *testing.T) { + b := NewUrlBuilder("x/:a/tt/:b?") + b.PathVariable("a", "1") + b.PathVariable("b", 2) + b.QueryVariable("c", 100) + b.QueryVariable("d", 1.1) + url := b.Build() + if url != "x/1/tt/2?c=100&d=1.1" { + t.Fatal(url) + } + + t.Log(url) + }) +} diff --git a/restutil/urlutil.go b/restutil/urlutil.go index b034482..23c71e5 100644 --- a/restutil/urlutil.go +++ b/restutil/urlutil.go @@ -16,7 +16,13 @@ package restutil -import "strings" +import ( + "bytes" + "fmt" + "net/url" + "reflect" + "strings" +) func QueryUrl(url string, params map[string]string) string { url = strings.TrimSpace(url) @@ -59,3 +65,107 @@ func PlaceholderUrl(url string, params map[string]string) string { return url } + +func Query(keyAndValue ...interface{}) (string, error) { + if len(keyAndValue) == 0 { + return "", nil + } + if len(keyAndValue)%2 != 0 { + return "", fmt.Errorf("Query parameter missing value, size is %d ", len(keyAndValue)) + } + m := make(map[string]interface{}, len(keyAndValue)/2) + for i := 0; i < len(keyAndValue); i += 2 { + if k, ok := keyAndValue[i].(string); ok { + m[k] = keyAndValue[i+1] + } else { + fmt.Errorf("Query key must be string, but get %s ", reflect.TypeOf(keyAndValue[i]).String()) + } + } + return EncodeQuery(m), nil +} + +func EncodeQuery(keyAndValue map[string]interface{}) string { + if len(keyAndValue) == 0 { + return "" + } + buf := bytes.Buffer{} + for k, v := range keyAndValue { + buf.WriteString(url.QueryEscape(fmt.Sprintf("%v", k))) + buf.WriteString("=") + buf.WriteString(url.QueryEscape(fmt.Sprintf("%v", v))) + buf.WriteString("&") + } + format := buf.String() + format = format[:len(format)-1] + return format +} + +func ReplaceUrl(uri string, leftDelim string, rightDelim string, keyAndValue map[string]interface{}) string { + if len(keyAndValue) == 0 { + return uri + } + for k, v := range keyAndValue { + uri = strings.Replace(uri, fmt.Sprintf("%s%v%s", leftDelim, k, rightDelim), url.QueryEscape(fmt.Sprintf("%v", v)), -1) + } + return uri +} + +type UrlBuilder struct { + url string + leftDelim string + rightDelim string + path map[string]interface{} + query map[string]interface{} +} + +func NewUrlBuilder(url string) *UrlBuilder { + return &UrlBuilder{ + url: url, + leftDelim: ":", + } +} + +func (b *UrlBuilder) WithDelim(leftDelim, rightDelim string) *UrlBuilder { + b.leftDelim = leftDelim + b.rightDelim = rightDelim + return b +} + +func (b *UrlBuilder) PathVariable(key string, value interface{}) *UrlBuilder { + if b.path == nil { + b.path = map[string]interface{}{} + } + b.path[key] = value + return b +} + +func (b *UrlBuilder) QueryVariable(key string, value interface{}) *UrlBuilder { + if b.query == nil { + b.query = map[string]interface{}{} + } + b.query[key] = value + return b +} + +func (b *UrlBuilder) Build() string { + buf := strings.Builder{} + if len(b.path) > 0 { + buf.WriteString(ReplaceUrl(b.url, b.leftDelim, b.rightDelim, b.path)) + } else { + buf.WriteString(b.url) + } + if len(b.query) > 0 { + query := EncodeQuery(b.query) + if b.url[len(b.url)-1] == '?' { + buf.WriteString(query) + } else { + buf.WriteString("?") + buf.WriteString(query) + } + } + return buf.String() +} + +func (b *UrlBuilder) String() string { + return b.Build() +} diff --git a/test/restclient_test.go b/test/restclient_test.go index fd3831b..3f1954a 100644 --- a/test/restclient_test.go +++ b/test/restclient_test.go @@ -482,3 +482,17 @@ func TestErrorStruct(t *testing.T) { t.Log(ret) }) } + +func TestUrlBuilder(t *testing.T) { + b := restclient.NewUrlBuilder("x/:a/tt/:b?") + b.PathVariable("a", "1") + b.PathVariable("b", 2) + b.QueryVariable("c", 100) + b.QueryVariable("d", 1.1) + url := b.Build() + if url != "x/1/tt/2?c=100&d=1.1" { + t.Fatal(url) + } + + t.Log(url) +}