isuperagent

package module
v0.0.0-...-cb501de Latest Latest
Warning

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

Go to latest
Published: Jan 17, 2020 License: MIT Imports: 15 Imported by: 0

README

isuperagent

Build Status

isuperagent 是一个通用、灵活的 HTTP CLIENT 库,包装了请求、响应参数。提供了灵活的属性,支持自定义 parser、支持组件(洋葱模型)、链式调用等特性。

示例

timeMiddleware, err := isuperagent.NewMiddleware("request_time")
basicAuthMiddleware, err := isuperagent.NewMiddleware("basic_auth", "username", "password")
debugMiddleware, err := isuperagent.NewMiddleware("debug", func(ctx isuperagent.Context) {
    log.Println(fmt.Sprintf("req headers: %+v", ctx.GetReq().GetHeaders()))
})

res, err := isuperagent.NewRequest().Get("http://localhost:8080/").Middleware(timeMiddleware, basicAuthMiddleware, debugMiddleware).Do()

安装

go get github.com/charleslxh/isuperagent

特性

  1. 支持中间件(洋葱模型)。
  2. 支持灵活的请求解析器 body parser,并且支持自定义解析器、覆盖现有解析器功能。
  3. 封装了请求、响应属性。
  4. 支持链式调用。
  5. 支持 HTTPS 调用请求、支持 SSL 单向认证、双向认证。
  6. 支持出错重试机制。
中间件

支持 中间件ispueragent 的最大特色,灵活的中间件能给开发者带来极大的便捷性、可操作性。

洋葱模型出自 NodeJS 的 WEB 框架 Koaisuperagent 由参考该框架的中间件思想构成。

参考资料:洋葱模型

整体设计思想如下:

用户调用时,发起请求之前(调用 .Do() 为发起请求)用户可以通过 Middleware 函数注册中间件,中间件的请求调用顺序(在发送请求之前)与注册时的顺序一致, 中间件的响应调用顺序(在返回结果之前)与注册时的顺序相反,具体链路如下:

发起请求 -> middleware A -> middleware B -> 请求服务器 -> middleware B -> middleware A -> 返回响应

洋葱模型

注意:请求响应和错误需要中间件一层一层的向上返回,如果中间某个中间件没有返回,则响应会被该中间价吞噬,错误也会被隐藏。

如何自定义中间件
  1. 自定义中间件必须实现 isuperagent.Middleware 签名。

    func(ctx isuperagent.Context, next isuperagent.Next) error {
       return nil
    }
    
  2. 中间件需要返回响应体和错误,如果你不关心,可以直接吞噬掉。

    func(ctx isuperagent.Context, next isuperagent.Next) error {
        mockRes := &Response{StatusCode: 404, StatusText: "Not Found"}
        ctx.SetRes(mockRes)
    
        return nil
    }
    
  3. 中间件提供 next 函数,用于调用下一个中间件,如果你不需要继续调用下一个中间件,则不需要调用该函数。

    func(ctx isuperagent.Context, next isuperagent.Next) error {
        return next()
    }
    

    注意:在 isuperagent 中,所有都是有中间件组成,请求的执行本质上就是中间件的遍历过程,所以最终发送请求的过程也是一个中间件,该中间价是内置在最末端的,所以,如果你中间某个中间件没有调用 next 方法,则请求也将不会发生,建议必须调用 next 函数,防止中间件链路完整

提示:可以参考现有的中间件写法。

中间件如何创建

isuperagent 支持中间件本质上就是一个签名为 func(ctx isuperagent.Context, next isuperagent.Next) error 的函数,任何改签名的函数都能注入。

func(ctx isuperagent.Context, next isuperagent.Next) error {
    start := time.Now()
    defer func() {
    	duration := fmt.Sprintf("%s", time.Now().Sub(start))
    	ctx.Set("request_time", duration)
    	ctx.GetRes().GetHeaders().Set("X-SuperAgent-Duration", duration)
    }()
    
    return next()
}

提示:可以参考现有的中间件工厂方法写法。

如何为中间件绑定不同的参数

isuperagent 提供了工厂方法来统一生产中间件,前提是必须向工厂注册你的中间件,isuperagent 内置一系列的中间件。

  1. 创建工厂方法,函数签名必须是 func NewTimeMiddlewareFactory(v ...interface{}) (isuperagent.Middleware, error)

    // Middleware: record the request duration
    func NewTimeMiddlewareFactory(v ...interface{}) (isuperagent.Middleware, error) {
    	headerName := "x-SuperAgent-Duration"
    	if len(v) > 0 {
    		if h, ok := v[0].(string); ok {
    			headerName = h
    		} else {
    			return nil, errors.New(fmt.Sprintf("excepted header_name is string, but got %v(%s)", v[0], reflect.TypeOf(v[0])))
    		}
    	}
    
    	return func(ctx isuperagent.Context, next isuperagent.Next) error {
    		start := time.Now()
    		defer func() {
    			duration := fmt.Sprintf("%s", time.Now().Sub(start))
    			ctx.Set("request_time", duration)
    			ctx.GetRes().GetHeaders().Set("X-SuperAgent-Duration", duration)
    		}()
    
    		return next()
    	}, nil
    }
    
  2. 注册工厂方法,工厂方法必须注册之后才能使用,配合 init 函数

    func init() {
    	isuperagent.RegisterMiddlewareFactory("request_time", NewTimeMiddlewareFactory)
    }
    
  3. 创建中间件实例。

    middleware, err := isuperagent.NewMiddleware("request_time")
    if err != nil {
    	return nil, err
    }
    

    或者不使用 isuperagent 提供的统一工厂函数。

    middleware, err := NewTimeMiddlewareFactory()
    if err != nil {
    	return nil, err
    }
    

提示:可以参考现有的中间件工厂方法写法。

中间件如何应用

中间件的使用需要在初始化请求的时候(发送请求之前 调用 Request.Do() 函数)注册,具体参考以下代码:

res, err := isuperagent.NewRequest().Get("http://localhost:8080/").Middleware(timeMiddleware, basicAuthMiddleware, debugMiddleware).Do()
解析器 BodyParser

isuperagent 提供了多样化的 bodyParser,如 htmljsonxml 等多种解析器。

解析器 bodyParser 其实就是序列化请求对象 request body、反序列化 response body 的组件。该组件通过请求头的 content-type 值会自动调用对应的 Parser 来解析请求/响应内容,如果没有定义响应的解析器,会使用默认的文本解析器(text/plain)解析。

用户可以自定义解析器,通过自定义解析器可以新增解析逻辑、或者覆盖默认的解析逻辑,灵活且强大。

如何自定义解析器
  1. util/isuperagent/bodyParser 目录下新建解析器的 go 文件。

    $ touch your/path/xxxx_parser.go
    
  2. 解析器必须实现 bodyParser.BodyParserInterface 接口。

    type XxxParser struct{}
    
    func (p *XxxParser) Unmarshal(data []byte, v interface{}) error {
        // TODO: add you Unmarshal logic code   
    	return nil
    }
    
    func (p *XxxParser) Marshal(v interface{}) ([]byte, error) {
    	// TODO: add you Marshal logic code   
    	return bs, nil
    }
    
    • Unmarshal 函数用于反序列化响应体。
    • Marshal 用于序列化请求体。
  3. 注册解析器,只有注册过的解析器才能生效,第一个参数为别名,第二个参数为所对应的 content-type 值,第三个为解析器实例。

    func init() {
    	Register("json", []string{
    		"application/json",
    		"application/javascript",
    		"application/ld+json",
    	}, &JsonParser{})
    }
    

提示:请参考其他解析器的写法。

如何应用解析器
  1. 请求体的序列化是自动的,你不需要关心它,通过不同的 content-type 可以调用不同的解析器。

  2. 响应体的反序列化,需要手动调用,具体参考如下方式。

    type ResponeData struct {
        Code int                 `json:"code"`
        Msg  string              `json:"msg"`
        Data map[string][]string `json:"data"`
    }
    
    res, err = isuperagent.NewRequest().Get("http://localhost:28080/v1/getHeader").Headers(headers).Do()
    if err != nil {
        return nil, err
    }
    
    var data ResponeData
    err = res.GetBody().Unmarshal(&data)
    if err != nil {
        return nil, err
    }
    log.Println(fmt.Sprintf("%+v", data))
    

    程序会自动调用对应的解析器解析响应体。

链式调用

isuperagent 支持链式调用,方便简洁,如下示例:

res, err := isuperagent.NewRequest().
    Post("http://localhost:8080").
    SetHeader("a", "1").
    SetHeaders(map[string]string{
        "a": 2,
        "b": 3,
    }).
    SetQuery("token", "3ausdygiausyd1").
    SetQueries(map[string]string{
        "key1": "v1",
        "key2": "v2",
    }).
    SetTimeout(5 * time.Second).
    SetRetry(5).
    Middleware(middlewareA, middlewareB, middlewareC).
    Do()

if err != nil {
    return nil, err
}

return res, err
SSL 请求

isuperagent 支持 HTTPS 请求、支持单向认证、支持双向认证。

  1. 发起 HTTPS 请求

    isuperagent.NewRequest().Get("https://www.baidu.com")
    
  2. 支持自签名服务器请求,忽略未知机构签发的证书(自签名)

    isuperagent.NewRequest().SetInsecureSkipVerify(true).Get("https://self-signed-cert-server.com")
    
  3. 支持单向认证

    isuperagent.NewRequest().SetCa("your/server_root_ca/path").Get("https://self-signed-cert-server.com")
    
  4. 支持双向认证

    isuperagent.NewRequest().SetCa("your/server_root_ca/path").SetCert("your/client_cert/path", "your/client_key/path").Get("https://self-signed-cert-server.com")
    
请求出错重试

isuperagent 支持出错重试机制。

isuperagent.NewRequest().Get("http://unknown-server.com").SetRetry(3).Do()

注意:所有中间件只会触发一次,与重试次数无关。

丰富的请求属性

具体属性查看 request.goresponse.go 文件。

其他

  1. 拥有足够的测试用例保证功能完善。
  2. 压力测试(待完善)。

Documentation

Index

Constants

View Source
const (
	Method_GET    = "GET"
	Method_POST   = "POST"
	Method_HEAD   = "HEAD"
	Method_PUT    = "PUT"
	Method_UPDATE = "UPDATE"
	Method_DELETE = "DELETE"
)

Variables

This section is empty.

Functions

func Compose

func Compose(ctx Context, middleware []Middleware) func() error

Composer all middleware Return the start middleware

func RegisterMiddlewareFactory

func RegisterMiddlewareFactory(name string, factory MiddlewareFactory)

Register your middleware to factory pool

Types

type Body

type Body struct {
	// contains filtered or unexported fields
}

func (*Body) GetData

func (b *Body) GetData() []byte

func (*Body) Unmarshal

func (b *Body) Unmarshal(v interface{}) error

type BodyInterface

type BodyInterface interface {
	GetRaw() string
	Unmarshal(v interface{}) error
}

type ContentType

type ContentType struct {
	MediaType string
	Charset   string
	Boundary  string
}

func ParseContentType

func ParseContentType(raw string) ContentType

func (*ContentType) String

func (c *ContentType) String() string

type Context

type Context interface {
	Set(key, value interface{}) Context
	Get(key interface{}) interface{}

	SetReq(Request) Context
	GetReq() Request
	SetRes(Response) Context
	GetRes() Response
}

func NewContext

func NewContext(ctx context.Context, req Request, res Response) Context

type Middleware

type Middleware func(ctx Context, next Next) error

func NewBasicAuthMiddlewareFactory

func NewBasicAuthMiddlewareFactory(v ...interface{}) (Middleware, error)

Middleware: HTTP Basic Auth

func NewDebugMiddlewareFactory

func NewDebugMiddlewareFactory(v ...interface{}) (Middleware, error)

Middleware: this middleware allow you to debug request information and response

func NewMiddleware

func NewMiddleware(name string, v ...interface{}) (Middleware, error)

The factory method to create a new middleware tip: you must register your middleware firstly by invoke isuperagent.RegisterMiddleware() method

func NewRequestExecMiddlewareFactory

func NewRequestExecMiddlewareFactory(v ...interface{}) (Middleware, error)

Middleware: send request

The last of middleware chain, it will call after all middleware. This middleware will do the following duties: 1. Generate the request object. 2. Set all request options, include queries, headers, bodies, authorization. 3. Send request, Generate response object. 4. Retry strategy when failure. Then return *Response, error to previous middleware.

func NewTimeMiddlewareFactory

func NewTimeMiddlewareFactory(v ...interface{}) (Middleware, error)

Middleware: record the request duration

type MiddlewareFactory

type MiddlewareFactory func(v ...interface{}) (Middleware, error)

Middleware factory method it is convenient to create an middleware

type Next

type Next func() error

The method to call the next middleware Only middleware call next() method, the next middleware will invoke At last, the request will send to target server

type Request

type Request interface {
	SetMethod(method string, options ...interface{}) Request
	GetMethod() string

	Get(url string, options ...interface{}) Request
	Post(url string, options ...interface{}) Request
	Head(url string, options ...interface{}) Request
	Put(url string, options ...interface{}) Request
	Update(url string, options ...interface{}) Request
	Delete(url string, options ...interface{}) Request

	SetUrl(url string) Request
	GetUrl() *URL
	GetRawUrl() string

	GetHeader(name string) string
	SetHeader(name, value string) Request
	GetHeaders() http.Header
	SetHeaders(kv map[string]string) Request

	SetContentType(contentType string) Request

	GetQuery(name string) string
	SetQuery(name string, value string) Request
	GetQueries() url.Values
	SetQueries(kv map[string]string) Request

	SetBody(v interface{}) Request
	GetBody() interface{}
	GetBodyRaw() ([]byte, error)

	SetTimeout(d time.Duration) Request
	GetTimeout() time.Duration
	SetRetry(times int) Request
	GetRetry() int

	SetInsecureSkipVerify(insecureSkipVerify bool) Request
	GetInsecureSkipVerify() bool
	SetTlsConfig(tlsConfig *tls.Config) Request
	GetTlsConfig() *tls.Config
	SetCa(caPath string) Request
	GetCa() string
	SetCert(certPath, keyPath string) Request
	GetCert() (string, string)
	BasicAuth(name, pass string) Request
	GetUsername() string
	GetPassword() string

	SetContext(ctx context.Context) Request

	Middleware(middleware ...Middleware) Request

	Do() (Response, error)
}

func NewRequest

func NewRequest() Request

func NewRequestWithContext

func NewRequestWithContext(ctx context.Context) Request

type Response

type Response interface {
	IsOk() bool
	GetHeaders() http.Header
	GetBody() *Body
	ParseBody(v interface{}) error
	GetStatusCode() int
	GetStatusText() string

	GetHttpRequest() *http.Request
	GetHttpResponse() *http.Response
}

func NewResponse

func NewResponse(req *http.Request, resp *http.Response) (Response, error)

type URL

type URL struct {
	*url.URL
	Queries url.Values
}

func NewURL

func NewURL() *URL

func (*URL) String

func (u *URL) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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