httpc

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2019 License: MIT Imports: 18 Imported by: 3

README

httpc简介

httpc这是一个发起http请求的客户端库。

它具有的特色包括:简单易用、易于扩展、支持链式调用、支持多种请求和响应格式的处理等。

特别适合用来调用RESTful风格的接口。

下载

go get github.com/recallsong/httpc

Api文档

查看 在线Api文档

我们也可以利用godoc工具在本地查看api文档:

godoc -http=:9090

在浏览器中查看地址:

http://localhost:9090/pkg/github.com/recallsong/httpc

快速入门

最简单的使用方式

var resp string
// GET http://localhost/hello?name=RecallSong
err := httpc.New("http://localhost").Path("hello").Query("name", "RecallSong").Get(&resp)
if err != nil {
    fmt.Println(resp) // 以字符串方式获取响应的数据
} else {
    fmt.Println(err)
}
var resp string
err := httpc.New("http://localhost").Path("/hello").Query("param", "value").
    Header("MyHeader", "HeaderValue").
    AddCookie(&http.Cookie{Name: "cookieName", Value: "cookieValue"}).
    Body("body data").Post(&resp)
if err != nil {
    fmt.Println(resp) // 以字符串方式获取响应的数据
} else {
    fmt.Println(err)
}

发送和接收json格式的数据

使用map传递数据
body := map[string]interface{}{
    "name": "RecallSong",
    "age":  18,
}
var resp map[string]interface{}
// 根据请求的Content-Type自动对数据进行转换
err := httpc.New("http://localhost").Path("json").
    ContentType(httpc.TypeApplicationJson).
    Body(body). // body转变为 {"name":"RecallSong","age":18}
    Post(&resp) // 根据响应中的Content-Type,将返回的数据解析到resp中
fmt.Println(err, resp)

// 如果请求或响应没有指定Content-Type,或是不正确,也可以强制指定转换格式类型
err = httpc.New("http://localhost").Path("json").
    Body(body, httpc.TypeApplicationJson). // body转变为 {"name":"RecallSong","age":18}
    Post(&resp, httpc.TypeApplicationJson) // 将返回的数据按json格式解析到map中
fmt.Println(err, resp)
使用struct传递数据
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}
body := Person{Name: "RecallSong", Age: 18}
var resp Person
err := httpc.New("http://localhost").Path("json").
    Body(body, httpc.TypeApplicationJson).
    Post(&resp, httpc.TypeApplicationJson)
fmt.Println(err, resp)

发送和接收xml格式的数据

type Person struct {
    Name string `xml:"name"`
    Age  int    `xml:"age"`
}
body := Person{Name: "RecallSong", Age: 18}
var resp Person
err := httpc.New("http://localhost").Path("xml").
    Body(body, httpc.TypeApplicationXml). // 数据转变为xml格式
    Post(&resp, httpc.TypeApplicationXml)
fmt.Println(err, resp)

发送表单参数

使用结构体发送
sbody := struct {
    Name string `form:"name"`
    Age  int    `form:"age"`
}{
    Name: "RecallSong",
    Age:  18,
}
var resp string
err := httpc.New("http://localhost").Path("echo").
    Body(sbody, httpc.TypeApplicationForm). // 将结构体转变为form格式的数据体
    Post(&resp)
fmt.Println(err, resp)
使用map发送
mbody := map[string]interface{}{
    "name": "RecallSong",
    "age":  19,
}
var resp string
err := httpc.New("http://localhost").Path("echo").
    Body(mbody, httpc.TypeApplicationForm). // 将map变为form格式的数据体
    Post(&resp)
fmt.Println(err, resp)
使用url.Values发送
ubody := url.Values{}
ubody.Set("name", "RecallSong")
ubody.Set("age", "20")
var resp string
err := httpc.New("http://localhost").Path("echo").
    Body(ubody). // 将url.Values类型转变form格式的数据体
    Post(&resp)
fmt.Println(err, resp)

自动编码url路径参数

var resp string
// 可以自动编码url路径参数
err := httpc.New("http://localhost").EscapedPath("recall/Song").EscapedPath(18).Get(&resp)
// 请求地址为 http://localhost/recall%2FSong/18
fmt.Println(err, resp)

上传文件

方式1
file, err := os.Open("doc.go")
if err != nil {
    fmt.Println(err)
    return
}
defer file.Close()
body := map[string]interface{}{
    "file":  file,
    "name":  "RecallSong",
    "age":   18,
    "file2": httpc.FilePath("doc.go:hello.go"), //上传doc.go文件,参数名为file2,文件名为hello.go
}
var resp string
err = httpc.New("http://localhost").Path("echo").
    Body(body, httpc.TypeMultipartFormData).Post(&resp)
fmt.Println(err)
方式2
file, err := os.Open("doc.go")
if err != nil {
    fmt.Println(err)
    return
}
defer file.Close()
body := struct {
    Name    string         `form:"name"`
    Address []string       `form:"address"`
    Age     int            `form:"age"`
    File    *os.File       `form:"file" file:"hello.go"`
    File2   httpc.FilePath `form:"file2"`
}{
    Name:    "RecallSong",
    Address: []string{"HangZhou", "WenZhou"},
    Age:     18,
    File:    file,
    File2:   httpc.FilePath("doc.go:hello2.go"), //上传doc.go文件,参数名为file2,文件名为hello2.go
}
var resp string
err = httpc.New("http://localhost").Path("echo").
    Body(body, httpc.TypeMultipartFormData).Post(&resp)
fmt.Println(err)

接收响应数据

// 前面的例子我们知道了可以接收json和xml格式的数据,也可以接收数据到一个string变量中
// 除此之外,我们还可以有一下几种方式接收数据

// []byte 方式接收
var bytesResp []byte
err := httpc.New("http://localhost").Path("hello").Get(&bytesResp)
fmt.Println(err, bytesResp)

// *http.Response 方式接收
var resp *http.Response
err := httpc.New("http://localhost").Path("hello").Get(&resp)
if err != nil {
    fmt.Println(err)
} else {
    // 注意这种方式要关闭Body
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(err, string(body))
}

下载文件

方式1
// 默认方式保存文件
err := httpc.New("http://localhost").Path("echo").Body("content").Post(httpc.FilePath("download1.txt"))
fmt.Println(err)
方式2
err := httpc.New("http://localhost").Path("echo").Body("content").Post(&httpc.SaveInfo{
    Path:     "download2.txt",
    Override: true,
    Mode:     0777})
fmt.Println(err)

指定成功的http状态码

// 如果返回的状态码与指定的状态码不匹配,则返回一个error
err := httpc.New("http://localhost").Path("not_exist").
    SuccessStatus(200).Get(nil)
fmt.Println(err)
// Output:
// error http status 404 , expect 200

请求上下文

// 请求上下文中包含了每次请求的设置、连接设置等,所有请求应该尽量共享Context
// 我们可以设置回调通知的函数
ctx := httpc.NewContext().
    AddCbBeforeSend(func(client *httpc.HttpC, args ...interface{}) error {
        fmt.Println("before request")
        return nil
    }).
    AddCbAfterSend(func(client *httpc.HttpC, args ...interface{}) error {
        fmt.Println("after response")
        return nil
    }).
    AddCbOnError(func(client *httpc.HttpC, args ...interface{}) error {
        fmt.Println("on error")
        return nil
    }).
    SetConnectReadTimeout(30*time.Second, 30*time.Second)
var resp string
err := httpc.New("http://localhost").Path("hello").SetContext(ctx).Get(&resp)
fmt.Println(err, resp)

// 库默认生成了一个上下文实例 httpc.DefaultContext,它并没有加锁保护,所以尽量在所有请求前设置好它
// 改变httpc.DefaultContext会影响所有未调用过SetContext的请求
httpc.DefaultContext.SetConnectReadTimeout(30*time.Second, 30*time.Second)
err = httpc.New("http://localhost").Path("hello").Get(&resp)
fmt.Println(err, resp)

超时设置

err := httpc.New("http://localhost").Path("timeout").
    SetContext(httpc.NewContext().SetConnectReadTimeout(time.Second, time.Second)).
    Get(nil)
fmt.Println(err)

请求重试

err := httpc.New("http://not_exist/").Path("not_exist").
    SetContext(httpc.NewContext().AddCbOnRetring(func(c *httpc.HttpC, args ...interface{}) error {
        fmt.Printf("retring %v, next interval %v\n", args[0], args[1])
        return nil
    }).SetRetryConfig(3, time.Second, 2)). // 重试3次,重试时间间隔依次为:2s, 4s, 8s
    Get(nil)
fmt.Println(err)

// Output:
// retring 1, next interval 2s
// retring 2, next interval 4s
// retring 3, next interval 8s
// Get http://not_exist/not_exist: dial tcp: lookup not_exist: no such host

自定义请求或响应处理器

// httpc库已经注册了一些通用的请求和响应处理器,但我们也可以额外添加处理器
ctx := httpc.NewContext()
ctx.BodyReaders = httpc.NewBodyReaders()
ctx.BodyReaders.RespBodyTypeReaders[reflect.TypeOf((*int)(nil))] = func(resp *http.Response, reader io.ReadCloser, typ reflect.Type, out interface{}) error {
    output := out.(*int)
    *output = resp.StatusCode
    return nil
}
// 返回响应状态码
var status int
err := httpc.New("http://localhost").Path("hello").
    SetContext(ctx).
    Get(&status)
fmt.Println(err, status)
// Output:
// <nil> 200

其他特性

请参考Api文档

License

MIT

Documentation

Overview

Author: recallsong

Email: songruiguo@qq.com

Description: httpc是一个简单的发起http请求的客户端库

Index

Examples

Constants

View Source
const (
	POST    = "POST"
	GET     = "GET"
	HEAD    = "HEAD"
	PUT     = "PUT"
	DELETE  = "DELETE"
	PATCH   = "PATCH"
	OPTIONS = "OPTIONS"
)

支持的HTTP method

View Source
const (
	TypeTextHtml          = "text/html"
	TypeTextPlain         = "text/plain"
	TypeApplicationJson   = "application/json"
	TypeApplicationXml    = "application/xml"
	TypeApplicationForm   = "application/x-www-form-urlencoded"
	TypeApplicationStream = "application/octet-stream"
	TypeMultipartFormData = "multipart/form-data"
)

支持的ContentType类型

Variables

View Source
var DefaultContext = NewContext()
View Source
var (
	ErrReqBodyType = errors.New("invalid request body type")
)
View Source
var (
	ErrRespOutType = errors.New("invalid response output type")
)
View Source
var GlobalReqBodyMediaReaders = map[string]ReqBodyReader{
	TypeApplicationJson:   jsonReqBodyReader,
	TypeApplicationXml:    xmlReqBodyReader,
	TypeApplicationForm:   formValuesReqBodyReader,
	TypeMultipartFormData: multipartReqBodyReader,
}

GlobalReqBodyMediaReaders 默认的根据MediaType获取RequestBody的Reader映射

View Source
var GlobalReqBodyTypeReaders = map[reflect.Type]ReqBodyReader{
	reflect.TypeOf(""):              stringReqBodyReader,
	reflect.TypeOf([]byte(nil)):     bytesReqBodyReader,
	reflect.TypeOf(url.Values(nil)): urlValuesReqBodyReader,
}

GlobalReqBodyTypeReaders 默认的根据类型获取RequestBody的Reader映射

View Source
var GlobalRespBodyMediaReaders = map[string]RespBodyReader{
	TypeApplicationJson:   readAllRespWrapper(jsonRespBodyReader),
	TypeApplicationXml:    readAllRespWrapper(xmlRespBodyReader),
	TypeApplicationStream: streamRespBodyReader,
}

GlobalRespBodyMediaReaders 默认的根据MediaType获取ResponseBody的Reader映射

View Source
var GlobalRespBodyTypeReaders = map[reflect.Type]RespBodyReader{
	reflect.TypeOf((*string)(nil)):         readAllRespWrapper(stringRespBodyReader),
	reflect.TypeOf((*[]byte)(nil)):         readAllRespWrapper(bytesRespBodyReader),
	reflect.TypeOf((**http.Response)(nil)): responseReader,
	reflect.TypeOf((FilePath)("")):         downloadRespBodyReader,
	reflect.TypeOf((*SaveInfo)(nil)):       downloadRespBodyReader,
	reflect.TypeOf(SaveInfo{}):             downloadRespBodyReader,
}

GlobalRespBodyTypeReaders 默认的根据类型获取ResponseBody的Reader映射

Functions

This section is empty.

Types

type BodyReaders

type BodyReaders struct {
	ReqBodyTypeReaders   map[reflect.Type]ReqBodyReader  // 根据类型获取reader
	ReqBodyMediaReaders  map[string]ReqBodyReader        // 根据MediaType获取reader
	RespBodyTypeReaders  map[reflect.Type]RespBodyReader // 根据类型获取reader
	RespBodyMediaReaders map[string]RespBodyReader       // 根据MediaType获取reader
}

BodyReaders

func NewBodyReaders

func NewBodyReaders() *BodyReaders

NewBodyReaders 创建一个BodyReaders

type Callback

type Callback func(c *HttpC, args ...interface{}) error

Callback 以回调方式通知内部所发生的事件

type Context

type Context struct {
	Client      *http.Client // 请求的客户端
	BodyReaders *BodyReaders // 获取请求和响应中的body的reader

	CbBeforeSend  []Callback    // 在发送请求前调用
	CbAfterSend   []Callback    // 在发送请求后调用
	CbOnError     []Callback    // 在发生错误时调用
	CbOnRetring   []Callback    // 在请求重试时调用
	Retries       int           // 重试次数,-1表示一直重试
	RetryInterval time.Duration // 重试间隔
	RetryFactor   float64       // 重试因子,影响每次重试的时间间隔
}

Context http请求上下文,管理所有请求公用的对象

func NewContext

func NewContext() *Context

NewContext 创建一个Context实例

func (*Context) AddCbAfterSend

func (c *Context) AddCbAfterSend(cb Callback) *Context

AddCbAfterSend 添加发送请求后的通知回调函数

func (*Context) AddCbBeforeSend

func (c *Context) AddCbBeforeSend(cb Callback) *Context

AddCbBeforeSend 添加发送请求前的通知回调函数

func (*Context) AddCbOnError

func (c *Context) AddCbOnError(cb Callback) *Context

AddCbOnError 添加发生错误时的通知回调函数

func (*Context) AddCbOnRetring

func (c *Context) AddCbOnRetring(cb Callback) *Context

AddCbOnRetring 添加请求重试的通知回调函数

func (*Context) EnableCookie

func (c *Context) EnableCookie(enable bool) *Context

EnableCookie 启用CookieJar

func (*Context) SetCheckRedirect

func (c *Context) SetCheckRedirect(cr func(req *http.Request, via []*http.Request) error) *Context

SetCheckRedirect 设置CheckRedirect

func (*Context) SetClient

func (c *Context) SetClient(client *http.Client) *Context

SetClient 设置http.Client

func (*Context) SetConnectReadTimeout

func (c *Context) SetConnectReadTimeout(connectTimeout time.Duration, readTimeout time.Duration) *Context

SetConnectReadTimeout 设置请求的连接超时和读数据超时

func (*Context) SetProxy

func (c *Context) SetProxy(proxy func(*http.Request) (*url.URL, error)) *Context

SetProxy 设置请求代理

func (*Context) SetRetryConfig

func (c *Context) SetRetryConfig(retries int, interval time.Duration, factor float64) *Context

SetRetryConfig 设置重试的配置

func (*Context) SetTLSClientConfig

func (c *Context) SetTLSClientConfig(config *tls.Config) *Context

SetTLSClientConfig 设置TLSClientConfig

func (*Context) SetTotalTimeout

func (c *Context) SetTotalTimeout(timeout time.Duration) *Context

SetTotalTimeout 设置总超时,包括连接、所有从定向、读数据的时间,直到数据被读完

type FilePath

type FilePath string

FilePath 保存文件的路径

type HttpC

type HttpC struct {
	Context       *Context       // 上下文
	Request       *http.Request  // 请求
	Response      *http.Response // 响应
	BaseURL       string         // 请求url基地址
	URL           string         // 请求的url
	QueryData     url.Values     // 请求url的query参数
	SendMediaType string         // 请求的ContentType
	Data          interface{}    // 要发送的数据体
	Error         error          // 请求发生的错误
	SucStatus     int            // 指定成功的状态码,不匹配则以Error的形式返回
}

HttpC 发起http请求的Client

Example (Body_reader)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"reflect"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	ctx := httpc.NewContext()
	ctx.BodyReaders = httpc.NewBodyReaders()
	ctx.BodyReaders.RespBodyTypeReaders[reflect.TypeOf((*int)(nil))] = func(resp *http.Response, reader io.ReadCloser, typ reflect.Type, out interface{}) error {
		output := out.(*int)
		*output = resp.StatusCode
		return nil
	}
	// 返回响应状态码
	var status int
	err := httpc.New(baseUrl).Path("hello").
		SetContext(ctx).
		Get(&status)
	fmt.Println(err, status)

}
Output:

<nil> 200
Example (Context)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	ctx := httpc.NewContext().
		AddCbBeforeSend(func(client *httpc.HttpC, args ...interface{}) error {
			// fmt.Println(client.DumpRequest())
			fmt.Println("before request")
			return nil
		}).
		AddCbAfterSend(func(client *httpc.HttpC, args ...interface{}) error {
			// fmt.Println(client.DumpResponse())
			fmt.Println("after response")
			return nil
		}).
		AddCbOnError(func(client *httpc.HttpC, args ...interface{}) error {
			// fmt.Println(client.Error)
			fmt.Println("on error")
			return nil
		}).
		SetConnectReadTimeout(30*time.Second, 30*time.Second)
	var resp string
	err := httpc.New(baseUrl).Path("hello").Query("name", "Song").SetContext(ctx).SuccessStatus(200).Get(&resp)
	fmt.Println(err, resp)
}
Output:

before request
after response
<nil> Hello, Song
Example (Download)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"os"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	// 默认方式保存文件
	err := httpc.New(baseUrl).Body("xxx").Path("echo").Post(httpc.FilePath("download1.txt"))
	fmt.Println(err)
	_, err = os.Stat("download1.txt")
	if os.IsNotExist(err) {
		fmt.Println(err)
	}
	// 保存文件的另一种方式
	err = httpc.New(baseUrl).Body("zzz").Path("echo").Post(&httpc.SaveInfo{
		Path:     "download2.txt",
		Override: true,
		Mode:     0777})
	fmt.Println(err)
	_, err = os.Stat("download2.txt")
	if os.IsNotExist(err) {
		fmt.Println(err)
	}
}
Output:

Example (Dump)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	var resp []byte
	// 构建更复杂的请求
	err := httpc.New(baseUrl).Path("/dump").Query("param", "value").
		Header("MyHeader", "HeaderValue").
		AddCookie(&http.Cookie{Name: "cook", Value: "testcook"}).
		Body("body data").Post(&resp)
	fmt.Println(err)
	// fmt.Println(string(resp))

}
Output:

<nil>
Example (Error)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	err := httpc.New(baseUrl).Path("not_exist").
		SetContext(httpc.NewContext().AddCbOnError(func(client *httpc.HttpC, args ...interface{}) error {
			fmt.Println("on error: ", client.Error)
			return nil
		})).
		SuccessStatus(200).Get(nil)
	fmt.Println(err)
}
Output:

on error:  error http status 404 , expect 200
error http status 404 , expect 200
Example (FormBody)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	// struct body
	sbody := struct {
		Name string `form:"name"`
		Age  int    `form:"age"`
	}{
		Name: "RecallSong",
		Age:  18,
	}
	var resp string
	// 发送form参数
	err := httpc.New(baseUrl).Path("echo").
		Body(sbody, httpc.TypeApplicationForm). // 将结构体转变为form格式的数据体
		Post(&resp)
	fmt.Println(err)
	fmt.Println(resp)

	// map body
	mbody := map[string]interface{}{
		"name": "RecallSong",
		"age":  19,
	}
	err = httpc.New(baseUrl).Path("echo").
		Body(mbody, httpc.TypeApplicationForm). // 将map变为form格式的数据体
		Post(&resp)
	fmt.Println(err)
	fmt.Println(resp)

	// url.Values body
	ubody := url.Values{}
	ubody.Set("name", "RecallSong")
	ubody.Set("age", "20")
	err = httpc.New(baseUrl).Path("echo").
		Body(ubody). // 将url.Values类型转变form格式的数据体
		Post(&resp)
	fmt.Println(err)
	fmt.Println(resp)
}
Output:

<nil>
age=18&name=RecallSong
<nil>
age=19&name=RecallSong
<nil>
age=20&name=RecallSong
Example (Gzip)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	// 测试重试请求
	var resp string
	err := httpc.New(baseUrl).Path("gzip").Get(&resp)
	fmt.Println(err)
	fmt.Println(resp)

}
Output:

<nil>
Hello Hello Hello Hello
Example (Hello)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	var resp string
	// 请求 {baseUrl}/hello?name=RecallSong, 并将返回数据读入到resp变量
	err := httpc.New(baseUrl).Path("hello").Query("name", "RecallSong").Get(&resp)
	fmt.Println(err)
	fmt.Println(resp)
}
Output:

<nil>
Hello, RecallSong
Example (MapBody)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	body := map[string]interface{}{
		"name": "RecallSong",
		"age":  18,
	}
	var resp map[string]interface{}
	// 根据请求的Content-Type自动对数据进行转换
	err := httpc.New(baseUrl).Path("json").
		ContentType(httpc.TypeApplicationJson).
		Body(body). // body转变为 {"name":"RecallSong","age":18}
		Post(&resp) // 根据响应中的Content-Type,将返回的数据解析到resp中
	fmt.Println(err)
	fmt.Println(resp["name"], resp["age"])

	// 如果请求或响应没有指定Content-Type,或是错误的Content-Type,也可以强制指定转换格式类型
	err = httpc.New(baseUrl).Path("json").
		Body(body, httpc.TypeApplicationJson). // body转变为 {"name":"RecallSong","age":18}
		Post(&resp, httpc.TypeApplicationJson) // 将返回的数据按json格式解析到map中
	fmt.Println(err)
	fmt.Println(resp["name"], resp["age"])
}
Output:

<nil>
RecallSong 18
<nil>
RecallSong 18
Example (Mutipart_map)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"os"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	file, err := os.Open("doc.go")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()
	body := map[string]interface{}{
		"file":  file,
		"name":  "RecallSong",
		"age":   18,
		"file2": httpc.FilePath("doc.go:hello.go"), //上传doc.go文件,参数名为file2,文件名为hello.go
	}
	var resp string
	err = httpc.New(baseUrl).Path("echo").
		Body(body, httpc.TypeMultipartFormData).Post(&resp)
	fmt.Println(err)
	// fmt.Println(resp)

}
Output:

<nil>
Example (Mutipart_struct)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"os"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	file, err := os.Open("doc.go")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()
	body := struct {
		Name    string         `form:"name"`
		Address []string       `form:"address"`
		Age     int            `form:"age"`
		File    *os.File       `form:"file" file:"hello.go"`
		File2   httpc.FilePath `form:"file2"`
	}{
		Name:    "RecallSong",
		Address: []string{"HangZhou", "WenZhou"},
		Age:     18,
		File:    file,
		File2:   httpc.FilePath("doc.go:hello2.go"), //上传doc.go文件,参数名为file2,文件名为hello2.go
	}
	var resp string
	err = httpc.New(baseUrl).Path("echo").
		Body(body, httpc.TypeMultipartFormData).Post(&resp)

	fmt.Println(err)
	// fmt.Println(resp)

}
Output:

<nil>
Example (Path)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"strings"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	req := httpc.New(baseUrl).EscapedPath("recall/song").EscapedPath(18).DumpRequest()
	fmt.Println(strings.Contains(req, "/recall%2Fsong/18"))
}
Output:

true
Example (Response)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	var resp *http.Response
	// 请求 {baseUrl}/hello?name=RecallSong, 并将返回数据读入到resp变量
	err := httpc.New(baseUrl).Path("hello").Query("name", "RecallSong").Get(&resp)
	if err != nil {
		fmt.Println(err)
	} else {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		fmt.Println(resp.Status)
		fmt.Println(err, string(body))
	}

}
Output:

200 OK
<nil> Hello, RecallSong
Example (Retry)
package main

import (
	"fmt"
	"time"

	"github.com/recallsong/httpc"
)

func main() {
	// 测试重试请求
	err := httpc.New("http://not_exist/").Path("not_exist").
		SetContext(httpc.NewContext().AddCbOnRetring(func(c *httpc.HttpC, args ...interface{}) error {
			fmt.Printf("retring %v, next interval %v\n", args[0], args[1])
			return nil
		}).SetRetryConfig(3, time.Second, 2)).
		Get(nil)
	fmt.Println(err)

}
Output:

retring 1, next interval 2s
retring 2, next interval 4s
retring 3, next interval 8s
Get http://not_exist/not_exist: dial tcp: lookup not_exist: no such host
Example (StructBody)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

type Person struct {
	Name string `json:"name" form:"name" xml:"name"`
	Age  int    `json:"age" form:"age" xml:"age"`
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	body := Person{Name: "RecallSong", Age: 18}
	var resp Person
	// 可以使用结构体来传递数据
	err := httpc.New(baseUrl).Path("echo").
		Body(body, httpc.TypeApplicationJson).
		Post(&resp, httpc.TypeApplicationJson)
	fmt.Println(err)
	fmt.Println(resp.Name, resp.Age)
}
Output:

<nil>
RecallSong 18
Example (Timeout)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	// 测试读数据超时
	err := httpc.New(baseUrl).Path("timeout").
		SetContext(httpc.NewContext().SetConnectReadTimeout(time.Second, 1*time.Second)).
		Get(nil)
	fmt.Println(err != nil)
}
Output:

true
Example (XmlBody)
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"time"

	"github.com/recallsong/httpc"
)

type Handler struct {
	Method string
	Func   func(w http.ResponseWriter, r *http.Request)
}

var server *httptest.Server

func startServer() string {
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if handler, ok := routers[r.URL.Path]; ok {
			if handler.Method != "" && handler.Method != r.Method {
				http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
				return
			}
			handler.Func(w, r)
		} else {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		}
	}))
	return server.URL
}

func stopServer() {
	server.Close()
}

var routers = map[string]Handler{
	"/hello": Handler{
		Method: "GET",
		Func: func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, "+r.URL.Query().Get("name"))
		},
	},
	"/dump": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := httputil.DumpRequest(r, true)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			fmt.Fprintln(w, string(bytes))
		},
	},
	"/echo": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Write(bytes)
		},
	},
	"/json": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/json; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/xml": Handler{
		Method: "POST",
		Func: func(w http.ResponseWriter, r *http.Request) {
			bytes, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
			w.Header().Add("Content-Type", "application/xml; charset=UTF-8")
			w.Write(bytes)
		},
	},
	"/error": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		},
	},
	"/timeout": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(2 * time.Second)
			fmt.Fprintln(w, "Hello")
		},
	},
	"/gzip": Handler{
		Func: func(w http.ResponseWriter, r *http.Request) {
			var buffer bytes.Buffer
			gw := gzip.NewWriter(&buffer)
			fmt.Fprintln(gw, "Hello Hello Hello Hello")
			gw.Close()
			byt := buffer.Bytes()
			w.Header().Add("Content-Type", "text/html; charset=utf-8")
			w.Header().Add("Content-Length", fmt.Sprint(len(byt)))
			w.Header().Add("Content-Encoding", "gzip")
			w.Write(byt)
		},
	},
}

type Person struct {
	Name string `json:"name" form:"name" xml:"name"`
	Age  int    `json:"age" form:"age" xml:"age"`
}

func main() {
	baseUrl := startServer()
	defer stopServer()
	body := Person{Name: "RecallSong", Age: 18}
	var resp Person
	// 发送和接收xml数据
	err := httpc.New(baseUrl).Path("xml").
		Body(body, httpc.TypeApplicationXml). // 数据转变为xml格式
		Post(&resp)
	fmt.Println(err)
	fmt.Println(resp)
}
Output:

<nil>
{RecallSong 18}

func New

func New(baseUrl string) *HttpC

New 创建一个HttpC类型

func (*HttpC) AddCookie

func (c *HttpC) AddCookie(ck *http.Cookie) *HttpC

AddCookie 添加cookie

func (*HttpC) AddHeader

func (c *HttpC) AddHeader(key, value string) *HttpC

AddHeader 添加http的请求头

func (*HttpC) BasicAuth

func (c *HttpC) BasicAuth(userName, password string) *HttpC

BasicAuth 设置基于Basic认证的信息

func (*HttpC) Body

func (c *HttpC) Body(body interface{}, mediaType ...string) *HttpC

Body 设置请求数据体

func (*HttpC) ContentType

func (c *HttpC) ContentType(value string) *HttpC

ContentType 设置http头中的Content-Type

func (*HttpC) Delete

func (c *HttpC) Delete(out interface{}, mediaType ...string) error

Delete 发送Delete请求,并根据mediaType将结果解析到out中

func (*HttpC) DumpRequest

func (c *HttpC) DumpRequest(dumpBody ...bool) string

DumpRequest 将http请求明细输出为string

func (*HttpC) DumpResponse

func (c *HttpC) DumpResponse(dumpBody ...bool) string

DumpResponse 将http请求明细输出为string

func (*HttpC) EscapedPath

func (c *HttpC) EscapedPath(path interface{}) *HttpC

EscapedPath 对path进行url编码后,再追加到当前的url上

func (*HttpC) Get

func (c *HttpC) Get(out interface{}, mediaType ...string) error

Get 发送Get请求,并根据mediaType将结果解析到out中

func (*HttpC) Header

func (c *HttpC) Header(key, value string) *HttpC

Header 设置http的请求头,会覆盖已存在的请求头

func (*HttpC) Options

func (c *HttpC) Options(out interface{}, mediaType ...string) error

Options 发送Options请求,并根据mediaType将结果解析到out中

func (*HttpC) Patch

func (c *HttpC) Patch(out interface{}, mediaType ...string) error

Patch 发送Patch请求,并根据mediaType将结果解析到out中

func (*HttpC) Path

func (c *HttpC) Path(path string) *HttpC

Path 追加url路径

func (*HttpC) Post

func (c *HttpC) Post(out interface{}, mediaType ...string) error

Post 发送Post请求,并根据mediaType将结果解析到out中

func (*HttpC) Put

func (c *HttpC) Put(out interface{}, mediaType ...string) error

Put 发送Put请求,并根据mediaType将结果解析到out中

func (*HttpC) Query

func (c *HttpC) Query(name, value string) *HttpC

Query 增加url的query参数

func (*HttpC) Reset

func (c *HttpC) Reset() *HttpC

Reset 重置HttpC

func (*HttpC) SetContext

func (c *HttpC) SetContext(context *Context) *HttpC

SetContext 设置请求上下文

func (*HttpC) String

func (c *HttpC) String() string

String 将Httpc转换为字符串

func (*HttpC) SuccessStatus

func (c *HttpC) SuccessStatus(sucStatus int) *HttpC

SuccessStatus 设置成功的状态码

type ReqBodyReader

type ReqBodyReader func(req *http.Request, typ reflect.Type, data interface{}) (io.ReadCloser, error)

ReqBodyReader 根据类型将数据转换为reader

type RespBodyReader

type RespBodyReader func(resp *http.Response, reader io.ReadCloser, typ reflect.Type, out interface{}) error

RespBodyReader 根据类型将数据读取到out中

type SaveInfo

type SaveInfo struct {
	Path     string
	Mode     os.FileMode
	Override bool
}

SaveInfo 保存文件的设置

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL