From 13ff215e59b25dbdffffc39b718437c408a32d14 Mon Sep 17 00:00:00 2001 From: guonaihong Date: Wed, 18 Jan 2023 14:08:37 +0800 Subject: [PATCH] Url support template (#351) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * url支持模板 * 更新文档 --- .github/workflows/go.yml | 4 +- README.md | 39 +++++++++++++ dataflow/dataflow.go | 34 ++++++------ dataflow/gout.go | 7 +++ dataflow/req.go | 19 ++++--- dataflow/req_bench_test.go | 17 ++++++ dataflow/req_url_template_test.go | 91 +++++++++++++++++++++++++++++++ gout.go | 39 ++++++++----- 8 files changed, 208 insertions(+), 42 deletions(-) create mode 100644 dataflow/req_bench_test.go create mode 100644 dataflow/req_url_template_test.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 6e4aa93..3f8094c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.13 + - name: Set up Go 1.14 uses: actions/setup-go@v1 with: - go-version: 1.13 + go-version: 1.14 id: go - name: Check out code into the Go module directory diff --git a/README.md b/README.md index 3dd0b61..019ff32 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ gout 是go写的http 客户端,为提高工作效率而开发 - [quick start](#quick-start) - [API Examples](#api-examples) - [GET POST PUT DELETE PATH HEAD OPTIONS](#get-post-put-delete-path-head-options) + - [GET POST PUT DELETE PATH HEAD OPTIONS template](#get-post-put-delete-path-head-options-template) - [Query Parameters](#Query-Parameters) - [http header](#http-header) - [Set request header](#set-request-header) @@ -239,6 +240,44 @@ func main() { gout.OPTIONS(url).Do() } +``` +## GET POST PUT DELETE PATH HEAD OPTIONS template +```go +package main + +import ( + "github.com/guonaihong/gout" +) + +type testURLTemplateCase struct { + Host string +} + +func main() { + + url := "https://{{.Host}}" + // 发送GET方法 + gout.GET(url, testURLTemplateCase{Host:"www.qq.com"}).Do() + + // 发送POST方法 + gout.POST(url, testURLTemplateCase{Host:"www.github.com"}).Do() + + // 发送PUT方法 + gout.PUT(url, testURLTemplateCase{Host:"www.baidu.com"}).Do() + + // 发送DELETE方法 + gout.DELETE(url, testURLTemplateCase{Host:"www.google.com"}).Do() + + // 发送PATH方法 + gout.PATCH(url, testURLTemplateCase{Host:"www.google.com"}).Do() + + // 发送HEAD方法 + gout.HEAD(url, testURLTemplateCase{Host:"www.google.com"}).Do() + + // 发送OPTIONS + gout.OPTIONS(url, testURLTemplateCase{Host:"www.google.com"}).Do() +} + ``` ## Query Parameters diff --git a/dataflow/dataflow.go b/dataflow/dataflow.go index 2b7c2af..04ea630 100644 --- a/dataflow/dataflow.go +++ b/dataflow/dataflow.go @@ -37,44 +37,44 @@ type DataFlow struct { } // GET send HTTP GET method -func (df *DataFlow) GET(url string) *DataFlow { - df.Req = reqDef(get, cleanPaths(url), df.out) +func (df *DataFlow) GET(url string, urlStruct ...interface{}) *DataFlow { + df.Req = reqDef(get, cleanPaths(url), df.out, urlStruct...) return df } // POST send HTTP POST method -func (df *DataFlow) POST(url string) *DataFlow { - df.Req = reqDef(post, cleanPaths(url), df.out) +func (df *DataFlow) POST(url string, urlStruct ...interface{}) *DataFlow { + df.Req = reqDef(post, cleanPaths(url), df.out, urlStruct...) return df } // PUT send HTTP PUT method -func (df *DataFlow) PUT(url string) *DataFlow { - df.Req = reqDef(put, cleanPaths(url), df.out) +func (df *DataFlow) PUT(url string, urlStruct ...interface{}) *DataFlow { + df.Req = reqDef(put, cleanPaths(url), df.out, urlStruct...) return df } // DELETE send HTTP DELETE method -func (df *DataFlow) DELETE(url string) *DataFlow { - df.Req = reqDef(delete2, cleanPaths(url), df.out) +func (df *DataFlow) DELETE(url string, urlStruct ...interface{}) *DataFlow { + df.Req = reqDef(delete2, cleanPaths(url), df.out, urlStruct...) return df } // PATCH send HTTP PATCH method -func (df *DataFlow) PATCH(url string) *DataFlow { - df.Req = reqDef(patch, cleanPaths(url), df.out) +func (df *DataFlow) PATCH(url string, urlStruct ...interface{}) *DataFlow { + df.Req = reqDef(patch, cleanPaths(url), df.out, urlStruct...) return df } // HEAD send HTTP HEAD method -func (df *DataFlow) HEAD(url string) *DataFlow { - df.Req = reqDef(head, cleanPaths(url), df.out) +func (df *DataFlow) HEAD(url string, urlStruct ...interface{}) *DataFlow { + df.Req = reqDef(head, cleanPaths(url), df.out, urlStruct...) return df } // OPTIONS send HTTP OPTIONS method -func (df *DataFlow) OPTIONS(url string) *DataFlow { - df.Req = reqDef(options, cleanPaths(url), df.out) +func (df *DataFlow) OPTIONS(url string, urlStruct ...interface{}) *DataFlow { + df.Req = reqDef(options, cleanPaths(url), df.out, urlStruct...) return df } @@ -129,13 +129,13 @@ func (df *DataFlow) SetMethod(method string) *DataFlow { } // SetURL set url -func (df *DataFlow) SetURL(url string) *DataFlow { +func (df *DataFlow) SetURL(url string, urlStruct ...interface{}) *DataFlow { if df.Err != nil { return df } - if df.Req.url == "" && df.Req.req == nil && df.Req.method == "" { - df.Req = reqDef("", cleanPaths(url), df.out) + if df.Req.url == "" && df.Req.req == nil { + df.Req = reqDef(df.method, cleanPaths(url), df.out, urlStruct...) return df } diff --git a/dataflow/gout.go b/dataflow/gout.go index 69ab9d9..eb64bc0 100644 --- a/dataflow/gout.go +++ b/dataflow/gout.go @@ -37,37 +37,44 @@ func New(c ...*http.Client) *Gout { } // TODO 这一层可以直接删除 +// v0.3.3版本开始算起, v0.3.7版本将会删除 // GET send HTTP GET method func GET(url string) *DataFlow { return New().GET(url) } // POST send HTTP POST method +// v0.3.3版本开始算起, v0.3.7版本将会删除 func POST(url string) *DataFlow { return New().POST(url) } // PUT send HTTP PUT method +// v0.3.3版本开始算起, v0.3.7版本将会删除 func PUT(url string) *DataFlow { return New().PUT(url) } // DELETE send HTTP DELETE method +// v0.3.3版本开始算起, v0.3.7版本将会删除 func DELETE(url string) *DataFlow { return New().DELETE(url) } // PATCH send HTTP PATCH method +// v0.3.3版本开始算起, v0.3.7版本将会删除 func PATCH(url string) *DataFlow { return New().PATCH(url) } // HEAD send HTTP HEAD method +// v0.3.3版本开始算起, v0.3.7版本将会删除 func HEAD(url string) *DataFlow { return New().HEAD(url) } // OPTIONS send HTTP OPTIONS method +// v0.3.3版本开始算起, v0.3.7版本将会删除 func OPTIONS(url string) *DataFlow { return New().OPTIONS(url) } diff --git a/dataflow/req.go b/dataflow/req.go index b73c5ce..780567e 100644 --- a/dataflow/req.go +++ b/dataflow/req.go @@ -10,6 +10,7 @@ import ( "net/url" "reflect" "strings" + "text/template" "github.com/guonaihong/gout/core" "github.com/guonaihong/gout/debug" @@ -583,17 +584,17 @@ func modifyURL(url string) string { return fmt.Sprintf("http://%s", url) } -func reqDef(method string, url string, g *Gout) Req { +func reqDef(method string, url string, g *Gout, urlStruct ...interface{}) Req { + if len(urlStruct) > 0 { + var out strings.Builder + tpl := template.Must(template.New(url).Parse(url)) + tpl.Execute(&out, urlStruct[0]) + url = out.String() + } + r := Req{method: method, url: modifyURL(url), g: g} - //后面收敛GlobalSetting, 计划删除这个变量 - //先这么写, 控制影响的范围 + r.Setting = GlobalSetting - /* - r.Setting.NotIgnoreEmpty = GlobalSetting.NotIgnoreEmpty - r.Setting.Timeout = GlobalSetting.Timeout - r.Setting.Index = GlobalSetting.Index - r.TimeoutIndex = GlobalSetting.TimeoutIndex - */ return r } diff --git a/dataflow/req_bench_test.go b/dataflow/req_bench_test.go new file mode 100644 index 0000000..ee170bb --- /dev/null +++ b/dataflow/req_bench_test.go @@ -0,0 +1,17 @@ +package dataflow + +import "testing" + +func Benchmark_URL_Template(b *testing.B) { + + ts := createMethodEcho() + tc := testURLTemplateCase{Host: ts.URL, Method: "get"} + + for n := 0; n < b.N; n++ { + code := 0 + New().GET("{{.Host}}/{{.Method}}", tc).Code(&code).Do() + if code != 200 { + panic("code != 200") + } + } +} diff --git a/dataflow/req_url_template_test.go b/dataflow/req_url_template_test.go new file mode 100644 index 0000000..800ca40 --- /dev/null +++ b/dataflow/req_url_template_test.go @@ -0,0 +1,91 @@ +package dataflow + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +type testURLTemplateCase struct { + Host string + Method string +} + +func createMethodEcho() *httptest.Server { + router := func() *gin.Engine { + router := gin.New() + + router.GET("/get", func(c *gin.Context) { + c.String(200, "get") + }) + + router.POST("/post", func(c *gin.Context) { + c.String(200, "post") + }) + + router.PUT("/put", func(c *gin.Context) { + c.String(200, "put") + }) + + router.PATCH("/patch", func(c *gin.Context) { + c.String(200, "patch") + }) + + router.OPTIONS("/options", func(c *gin.Context) { + c.String(200, "options") + }) + + router.HEAD("/head", func(c *gin.Context) { + c.String(200, "head") + }) + + return router + }() + + return httptest.NewServer(http.HandlerFunc(router.ServeHTTP)) +} + +func Test_URL_Template(t *testing.T) { + ts := createMethodEcho() + for _, tc := range []testURLTemplateCase{ + {Host: ts.URL, Method: "get"}, + {Host: ts.URL, Method: "post"}, + {Host: ts.URL, Method: "put"}, + {Host: ts.URL, Method: "patch"}, + {Host: ts.URL, Method: "options"}, + {Host: ts.URL, Method: "head"}, + } { + body := "" + body2 := "" + switch tc.Method { + case "get": + New().GET("{{.Host}}/{{.Method}}", tc).BindBody(&body).Do() + case "post": + New().POST("{{.Host}}/{{.Method}}", tc).BindBody(&body).Do() + case "put": + New().PUT("{{.Host}}/{{.Method}}", tc).BindBody(&body).Do() + case "patch": + New().PATCH("{{.Host}}/{{.Method}}", tc).BindBody(&body).Do() + case "options": + New().OPTIONS("{{.Host}}/{{.Method}}", tc).BindBody(&body).Do() + case "head": + code := 0 + New().HEAD("{{.Host}}/{{.Method}}", tc).Debug(true).BindBody(&body).Code(&code).Do() + New().SetMethod(strings.ToUpper(tc.Method)).SetURL("{{.Host}}/{{.Method}}", tc).Debug(true).BindBody(&body2).Code(&code).Do() + assert.Equal(t, code, 200) + continue + } + + New().SetMethod(strings.ToUpper(tc.Method)).SetURL("{{.Host}}/{{.Method}}", tc).Debug(true).BindBody(&body2).Do() + assert.Equal(t, body, tc.Method) + b := assert.Equal(t, body2, tc.Method) + if !b { + return + } + } + +} diff --git a/gout.go b/gout.go index 8599187..3fef1ba 100644 --- a/gout.go +++ b/gout.go @@ -31,38 +31,49 @@ func New(c ...*http.Client) *dataflow.Gout { } // GET send HTTP GET method -func GET(url string) *dataflow.DataFlow { - return dataflow.GET(url) +// 第一种情况 +// gout.GET("wwww.demo.xx/test-appkey") +// +// 第二种情况 +// +// type host struct { +// Host string +// AppKey string +// } +// +// gout.GET("http://{{.Host}/{{.AppKey}}}", &host{Host:"www.demo.xx", AppKey:"test-appkey"}) +func GET(url string, urlStruct ...interface{}) *dataflow.DataFlow { + return dataflow.New().GET(url, urlStruct...) } // POST send HTTP POST method -func POST(url string) *dataflow.DataFlow { - return dataflow.POST(url) +func POST(url string, urlStruct ...interface{}) *dataflow.DataFlow { + return dataflow.New().POST(url, urlStruct) } // PUT send HTTP PUT method -func PUT(url string) *dataflow.DataFlow { - return dataflow.PUT(url) +func PUT(url string, urlStruct ...interface{}) *dataflow.DataFlow { + return dataflow.New().PUT(url, urlStruct) } // DELETE send HTTP DELETE method -func DELETE(url string) *dataflow.DataFlow { - return dataflow.DELETE(url) +func DELETE(url string, urlStruct ...interface{}) *dataflow.DataFlow { + return dataflow.New().DELETE(url, urlStruct) } // PATCH send HTTP PATCH method -func PATCH(url string) *dataflow.DataFlow { - return dataflow.PATCH(url) +func PATCH(url string, urlStruct ...interface{}) *dataflow.DataFlow { + return dataflow.New().PATCH(url, urlStruct) } // HEAD send HTTP HEAD method -func HEAD(url string) *dataflow.DataFlow { - return dataflow.HEAD(url) +func HEAD(url string, urlStruct ...interface{}) *dataflow.DataFlow { + return dataflow.New().HEAD(url, urlStruct) } // OPTIONS send HTTP OPTIONS method -func OPTIONS(url string) *dataflow.DataFlow { - return dataflow.OPTIONS(url) +func OPTIONS(url string, urlStruct ...interface{}) *dataflow.DataFlow { + return dataflow.New().OPTIONS(url, urlStruct) } // 设置不忽略空值