From c58445c77232ecad2b39169c12b306c2d1198b17 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 3 Aug 2013 22:20:09 +0800 Subject: [PATCH] add httplib support like http.client --- docs/zh/HttpLib.md | 78 ++++++++++ docs/zh/README.md | 1 + docs/zh/quickstart/cache.md | 0 docs/zh/quickstart/configs.md | 0 docs/zh/quickstart/controller.md | 0 docs/zh/quickstart/createapp.md | 0 docs/zh/quickstart/deploy.md | 0 docs/zh/quickstart/devmode.md | 0 docs/zh/quickstart/filter.md | 0 docs/zh/quickstart/flash.md | 0 docs/zh/quickstart/logs.md | 0 docs/zh/quickstart/miniapp.md | 0 docs/zh/quickstart/params.md | 0 docs/zh/quickstart/request.md | 0 docs/zh/quickstart/response.md | 0 docs/zh/quickstart/router.md | 0 docs/zh/quickstart/session.md | 0 docs/zh/quickstart/staticfile.md | 0 docs/zh/quickstart/template.md | 0 docs/zh/quickstart/third.md | 0 httplib.go | 242 +++++++++++++++++++++++++++++++ httplib_test.go | 32 ++++ 22 files changed, 353 insertions(+) create mode 100644 docs/zh/HttpLib.md create mode 100644 docs/zh/quickstart/cache.md create mode 100644 docs/zh/quickstart/configs.md create mode 100644 docs/zh/quickstart/controller.md create mode 100644 docs/zh/quickstart/createapp.md create mode 100644 docs/zh/quickstart/deploy.md create mode 100644 docs/zh/quickstart/devmode.md create mode 100644 docs/zh/quickstart/filter.md create mode 100644 docs/zh/quickstart/flash.md create mode 100644 docs/zh/quickstart/logs.md create mode 100644 docs/zh/quickstart/miniapp.md create mode 100644 docs/zh/quickstart/params.md create mode 100644 docs/zh/quickstart/request.md create mode 100644 docs/zh/quickstart/response.md create mode 100644 docs/zh/quickstart/router.md create mode 100644 docs/zh/quickstart/session.md create mode 100644 docs/zh/quickstart/staticfile.md create mode 100644 docs/zh/quickstart/template.md create mode 100644 docs/zh/quickstart/third.md create mode 100644 httplib.go create mode 100644 httplib_test.go diff --git a/docs/zh/HttpLib.md b/docs/zh/HttpLib.md new file mode 100644 index 00000000..09e9824b --- /dev/null +++ b/docs/zh/HttpLib.md @@ -0,0 +1,78 @@ +## 方便的http客户端 +我们经常会使用Go来请求其他API应用,例如你使用beego开发了一个RESTFul的API应用,那么如果来请求呢?当然可以使用`http.Client`来实现,但是需要自己来操作很多步骤,自己需要考虑很多东西,所以我就基于net下的一些包实现了这个简便的http客户端工具。 + +该工具的主要特点: + +- 链式操作 +- 超时控制 +- 方便的解析 +- 可控的debug + +## 例子 +我们上次开发的RESTful应用,最后我写过如何通过curl来进行测试,那么下面一一对每个操作如何用httplib来操作进行展示 + +- 添加一个对象: + + `curl -X POST -d '{"Score":1337,"PlayerName":"Sean Plott"}' http://127.0.0.1:8080/object` + + 返回一个相应的objectID:astaxie1373349756660423900 + + str,err:=beego.Post("http://127.0.0.1:8080/object").Body(`{"Score":1337,"PlayerName":"Sean Plott"}`).String() + if err != nil{ + println(err) + } + +- 查询一个对象 + + `curl -X GET http://127.0.0.1:8080/object/astaxie1373349756660423900` + + var object Obeject + err:=beego.Get("http://127.0.0.1:8080/object/astaxie1373349756660423900").ToJson(&object) + if err != nil{ + println(err) + } + +- 查询全部的对象 + + `curl -X GET http://127.0.0.1:8080/object` + + var objects []Object + err:=beego.Get("http://127.0.0.1:8080/object").ToJson(&objects) + if err != nil{ + println(err) + } + +- 更新一个对象 + + `curl -X PUT -d '{"Score":10000}'http://127.0.0.1:8080/object/astaxie1373349756660423900` + + str,err:=beego.Put("http://127.0.0.1:8080/object/astaxie1373349756660423900").Body(`{"Score":10000}`).String() + if err != nil{ + println(err) + } + +- 删除一个对象 + + `curl -X DELETE http://127.0.0.1:8080/object/astaxie1373349756660423900` + + str,er:=beego.Delete("http://127.0.0.1:8080/object/astaxie1373349756660423900").String() + if err != nil{ + println(err) + } + +## 开启调试模式 +用户可以开启调试打印request信息,默认是关闭模式 + + beego.Post(url).Debug(true) + +## ToFile、ToXML、ToJson +上面我演示了Json的解析,其实还有直接保存为文件的ToFile操作,解析XML的ToXML操作 + + +## 设置链接超时和读写超时 +默认都设置为60秒,用户可以通过函数来设置相应的超时时间 + + beego.Get(url).SetTimeout(100*time.Second,100*time.Second) + + +更加详细的请参考[API接口](http://gowalker.org/github.com/astaxie/beego) \ No newline at end of file diff --git a/docs/zh/README.md b/docs/zh/README.md index de22b025..a4233aff 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -44,6 +44,7 @@ beego是一个类似tornado的Go应用框架,采用了RESTFul的方式来实 * [beego案例](Application.md) * [热升级](HotUpdate.md) * [API应用开发入门](API.md) +* [HTTPLIB客户端](HttpLib.md) # API接口 diff --git a/docs/zh/quickstart/cache.md b/docs/zh/quickstart/cache.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/configs.md b/docs/zh/quickstart/configs.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/controller.md b/docs/zh/quickstart/controller.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/createapp.md b/docs/zh/quickstart/createapp.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/deploy.md b/docs/zh/quickstart/deploy.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/devmode.md b/docs/zh/quickstart/devmode.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/filter.md b/docs/zh/quickstart/filter.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/flash.md b/docs/zh/quickstart/flash.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/logs.md b/docs/zh/quickstart/logs.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/miniapp.md b/docs/zh/quickstart/miniapp.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/params.md b/docs/zh/quickstart/params.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/request.md b/docs/zh/quickstart/request.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/response.md b/docs/zh/quickstart/response.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/router.md b/docs/zh/quickstart/router.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/session.md b/docs/zh/quickstart/session.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/staticfile.md b/docs/zh/quickstart/staticfile.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/template.md b/docs/zh/quickstart/template.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/zh/quickstart/third.md b/docs/zh/quickstart/third.md new file mode 100644 index 00000000..e69de29b diff --git a/httplib.go b/httplib.go new file mode 100644 index 00000000..25d010e0 --- /dev/null +++ b/httplib.go @@ -0,0 +1,242 @@ +package beego + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httputil" + "net/url" + "os" + "strings" + "time" +) + +var defaultUserAgent = "beegoServer" + +func Get(url string) *BeegoHttpRequest { + var req http.Request + req.Method = "GET" + req.Header = http.Header{} + req.Header.Set("User-Agent", defaultUserAgent) + return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second} +} + +func Post(url string) *BeegoHttpRequest { + var req http.Request + req.Method = "POST" + req.Header = http.Header{} + req.Header.Set("User-Agent", defaultUserAgent) + return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second} +} + +func Put(url string) *BeegoHttpRequest { + var req http.Request + req.Method = "PUT" + req.Header = http.Header{} + req.Header.Set("User-Agent", defaultUserAgent) + return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second} +} + +func Delete(url string) *BeegoHttpRequest { + var req http.Request + req.Method = "DELETE" + req.Header = http.Header{} + req.Header.Set("User-Agent", defaultUserAgent) + return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second} +} + +func Head(url string) *BeegoHttpRequest { + var req http.Request + req.Method = "HEAD" + req.Header = http.Header{} + req.Header.Set("User-Agent", defaultUserAgent) + return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second} +} + +type BeegoHttpRequest struct { + url string + req *http.Request + params map[string]string + showdebug bool + connectTimeout time.Duration + readWriteTimeout time.Duration +} + +func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { + b.showdebug = isdebug + return b +} + +func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest { + b.connectTimeout = connectTimeout + b.readWriteTimeout = readWriteTimeout + return b +} + +func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest { + b.req.Header.Set(key, value) + return b +} + +func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest { + b.params[key] = value + return b +} + +func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { + switch t := data.(type) { + case string: + bf := bytes.NewBufferString(t) + b.req.Body = ioutil.NopCloser(bf) + b.req.ContentLength = int64(len(t)) + case []byte: + bf := bytes.NewBuffer(t) + b.req.Body = ioutil.NopCloser(bf) + b.req.ContentLength = int64(len(t)) + } + return b +} + +func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { + var paramBody string + if b.params != nil && len(b.params) > 0 { + var buf bytes.Buffer + for k, v := range b.params { + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v)) + buf.WriteByte('&') + } + paramBody = buf.String() + paramBody = paramBody[0 : len(paramBody)-1] + } + if b.req.Method == "GET" && len(paramBody) > 0 { + if strings.Index(b.url, "?") != -1 { + b.url += "&" + paramBody + } else { + b.url = b.url + "?" + paramBody + } + } else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 { + b.Header("Content-Type", "application/x-www-form-urlencoded") + b.Body(paramBody) + } + + url, err := url.Parse(b.url) + if url.Scheme == "" { + b.url = "http://" + b.url + url, err = url.Parse(b.url) + } + + if err != nil { + return nil, err + } + b.req.URL = url + if b.showdebug { + dump, err := httputil.DumpRequest(b.req, true) + if err != nil { + println(err.Error()) + } + println(string(dump)) + } + + client := &http.Client{ + Transport: &http.Transport{ + Dial: TimeoutDialer(b.connectTimeout, b.readWriteTimeout), + }, + } + resp, err := client.Do(b.req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (b *BeegoHttpRequest) String() (string, error) { + data, err := b.Bytes() + if err != nil { + return "", err + } + + return string(data), nil +} + +func (b *BeegoHttpRequest) Bytes() ([]byte, error) { + resp, err := b.getResponse() + if err != nil { + return nil, err + } + if resp.Body == nil { + return nil, nil + } + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return data, nil +} + +func (b *BeegoHttpRequest) ToFile(filename string) error { + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + resp, err := b.getResponse() + if err != nil { + return err + } + if resp.Body == nil { + return nil + } + defer resp.Body.Close() + _, err = io.Copy(f, resp.Body) + if err != nil { + return err + } + return nil +} + +func (b *BeegoHttpRequest) ToJson(v interface{}) error { + data, err := b.Bytes() + if err != nil { + return err + } + err = json.Unmarshal(data, v) + if err != nil { + return err + } + return nil +} + +func (b *BeegoHttpRequest) ToXML(v interface{}) error { + data, err := b.Bytes() + if err != nil { + return err + } + err = xml.Unmarshal(data, v) + if err != nil { + return err + } + return nil +} + +func (b *BeegoHttpRequest) Response() (*http.Response, error) { + return b.getResponse() +} + +func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) { + return func(netw, addr string) (net.Conn, error) { + conn, err := net.DialTimeout(netw, addr, cTimeout) + if err != nil { + return nil, err + } + conn.SetDeadline(time.Now().Add(rwTimeout)) + return conn, nil + } +} diff --git a/httplib_test.go b/httplib_test.go new file mode 100644 index 00000000..28b729ca --- /dev/null +++ b/httplib_test.go @@ -0,0 +1,32 @@ +package beego + +import ( + "io/ioutil" + "testing" +) + +func TestGetUrl(t *testing.T) { + resp, err := GetUrl("http://beego.me/").Response() + if err != nil { + t.Fatal(err) + } + if resp.Body == nil { + t.Fatal("body is nil") + } + data, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + t.Fatal(err) + } + if len(data) == 0 { + t.Fatal("data is no") + } + + str, err := GetUrl("http://beego.me/").String() + if err != nil { + t.Fatal(err) + } + if len(str) == 0 { + t.Fatal("has no info") + } +}