ghttp

package module
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2024 License: MIT Imports: 23 Imported by: 10

README

ghttp

golang http客户端

安装

go get -u github.com/zdz1715/ghttp@latest

使用

快速开始
package main

import (
  "context"
  "fmt"
  "net/http"

  "github.com/zdz1715/ghttp"
)

func main() {

  client := ghttp.NewClient(
    ghttp.WithEndpoint("https://gitlab.com"),
  )

  var reply any
  _, err := client.Invoke(context.Background(), http.MethodGet, "/api/v4/projects", nil, &reply, &ghttp.CallOptions{
    Query: map[string]any{
      "page": "1",
      //"membership": true,
    },
  })
  if err != nil {
    panic(err)
  }

  fmt.Printf("Invoke /api/v4/projects success, reply: %+v\n", reply)
}
选项
配置客户端的HTTP RoundTripper

WithTransport(trans http.RoundTripper) ClientOption

// example: 配置代理和客户端证书
ghttp.WithTransport(&http.Transport{
    Proxy: ghttp.ProxyURL(":7890"), // or http.ProxyFromEnvironment
    TLSClientConfig: &tls.Config{
        InsecureSkipVerify: true,
    },
}),
配置客户端的请求默认超时时间,若设置了单独超时时间,则优先使用单独超时时间

WithTimeout(d time.Duration) ClientOption

// example: 单独设置超时时间
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
defer cancel()
_, err := client.Invoke(ctx, http.MethodGet, "/api/v4/projects", nil, nil)
配置客户端的默认User-Agent

WithUserAgent(userAgent string) ClientOption

配置客户端默认访问的endpoint, 若单独请求一个完整URL,则优先使用单独的完整URL

WithEndpoint(endpoint string) ClientOption

// example: 单独设置完整URL,会忽略默认endpoint
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
defer cancel()
_, err := client.Invoke(ctx, http.MethodGet, "https://gitlab.com/api/v4/projects", nil, nil)
配置客户端的Content-type, 默认:application/json

WithContentType(contentType string) ClientOption

配置客户端代理,默认:http.ProxyFromEnvironment , 可使用辅助函数ghttp.ProxyURL(url)

WithProxy(f func(*http.Request) (*url.URL, error))

配置响应状态码不是2xx时,要bind的结构体, 也会直接返回错误,方便后续bind预期的响应数据

可自定义,需实现Not2xxError方法

WithNot2xxError(f func() Not2xxError) ClientOption

配置Debug选项

WithDebug(f func() DebugInterface) ClientOption

可自定义,需实现DebugInterface方法

// example: 
ghttp.WithDebug(func() ghttp.DebugInterface {
  return &ghttp.Debug{
    Writer: os.Stdout,
    Trace:  true, // 开启trace
    TraceCallback: func(w io.Writer, info ghttp.TraceInfo) { // trace完成时回调
        _, _ = w.Write(info.Table())
    },
  }
}),
调用

Invoke(ctx context.Context, method, path string, args any, reply any, opts ...CallOption) (*http.Response, error)

Do(req *http.Request, opts ...CallOption) (*http.Response, error)

Calloption是一个接口,会按添加顺序循环调用,只需实现以下方法即可定制

type CallOption interface {
    Before(request *http.Request) error
    After(response *http.Response) error
}
ghttp.CallOptions

实现了Calloption接口,主要实现以下功能

type CallOptions struct {
	// request
	Query any // set query

	// Auth
	Username string // Basic Auth
	Password string
	
	BearerToken string // Bearer Token

	// hooks
	BeforeHook func(request *http.Request) error
	AfterHook  func(response *http.Response) error
}

Bind

Request Query

支持以下类型:

  • string
  • map[any]any
  • []any
  • [...]any
  • struct
struct tag

query:"yourName,inline,omitempty,int,unix,del:delimiter,time_format:2006-01-02"

  • yourName: 自定义名字("-"则忽略),未设置则使用字段名,若是"-,"则"-"为名字
  • omitempty: 值为空则忽略
  • inline: ,inline会使子结构与父结构平级
  • int: bool类型时,true1false0
  • unix: time.time类型时,返回时间戳(秒)
  • unixmilli: time.time类型时,返回时间戳(毫秒)
  • unixnano: time.time类型时,返回时间戳(纳秒)
  • del: slicearray类型时,delimiter有以下值
    • comma: 使用","相连
    • space: 使用" "相连
    • semicolon: 使用";"相连
    • brackets: 形式:user[]=linda&user[]=liming
    • custom: 自定义的值,可以为任何值
  • time_format: 时间格式化字符串
Encoding

根据content-type自动加载对应的Codec实例,content-type会提取子部分类型,如:application/jsonapplication/vnd.api+json都为json,

自定义Codec

覆盖默认的json序列化,使用sonic

package main

import (
  "github.com/bytedance/sonic"
  "github.com/zdz1715/ghttp"
)

type codec struct{}

func (codec) Name() string {
  return "sonic-json"
}

func (codec) Marshal(v interface{}) ([]byte, error) {
  return sonic.Marshal(v)
}

func (codec) Unmarshal(data []byte, v interface{}) error {
  return sonic.Unmarshal(data, v)
}

func main() {
  ghttp.RegisterCodecByContentType("application/json", codec{})
}

Debug

设置WithDebug开启

ghttp.WithDebug(ghttp.DefaultDebug)
--------------------------------------------
Trace                         Value                         
--------------------------------------------
DNSDuration                   3.955292ms                    
ConnectDuration               102.718541ms                  
TLSHandshakeDuration          98.159333ms                   
RequestDuration               138.834µs                     
WaitResponseDuration          307.559875ms                  
TotalDuration                 412.40375ms                   
--------------------------------------------
* Host gitlab.com:443 was resolved.
* IPv4: 198.18.7.159
*   Trying 198.18.7.159:443...
* Connected to gitlab.com (198.18.7.159) port 443
* SSL connection using TLS 1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
* using HTTP/1.1
> POST /oauth/token HTTP/1.1
> User-Agent: sdk/gitlab-v0.0.1
> Accept: application/json
> Content-Type: application/json
> Beforehook: BeforeHook
> Authorization: Basic Z2l0bGFiOnBhc3N3b3Jk
>

{
    "client_id": "app",
    "grant_type": "password"
}

> HTTP/2.0 401 Unauthorized
> Content-Security-Policy: base-uri 'self'; child-src https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://www.googletagmanager.com/ns.html https://*.zuora.com/apps/PublicHostedPageLite.do https://gitlab.com/admin/ https://gitlab.com/assets/ https://gitlab.com/-/speedscope/index.html https://gitlab.com/-/sandbox/ 'self' https://gitlab.com/assets/ blob: data:; connect-src 'self' https://gitlab.com wss://gitlab.com https://sentry.gitlab.net https://new-sentry.gitlab.net https://customers.gitlab.com https://snowplow.trx.gitlab.net https://sourcegraph.com https://collector.prd-278964.gl-product-analytics.com; default-src 'self'; font-src 'self'; form-action 'self' https: http:; frame-ancestors 'self'; frame-src https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://www.googletagmanager.com/ns.html https://*.zuora.com/apps/PublicHostedPageLite.do https://gitlab.com/admin/ https://gitlab.com/assets/ https://gitlab.com/-/speedscope/index.html https://gitlab.com/-/sandbox/; img-src 'self' data: blob: http: https:; manifest-src 'self'; media-src 'self' data: blob: http: https:; object-src 'none'; report-uri https://new-sentry.gitlab.net/api/4/security/?sentry_key=f5573e26de8f4293b285e556c35dfd6e&sentry_environment=gprd; script-src 'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/ https://apis.google.com https://*.zuora.com/apps/PublicHostedPageLite.do 'nonce-HZQNGx99dfvcmkJEPBTxvQ=='; style-src 'self' 'unsafe-inline'; worker-src 'self' https://gitlab.com/assets/ blob: data:
> Vary: Origin
> X-Request-Id: 01HZ491CCGAH2VB7AC4JB34V9P
> Strict-Transport-Security: max-age=31536000
> Date: Thu, 30 May 2023 08:14:37 GMT
> Content-Type: application/json; charset=utf-8
> Referrer-Policy: strict-origin-when-cross-origin
> X-Content-Type-Options: nosniff
> X-Permitted-Cross-Domain-Policies: none
> X-Runtime: 0.019682
> X-Xss-Protection: 0
> Nel: {"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}
> Cf-Ray: 88bd45882b400491-HKG
> Www-Authenticate: Bearer realm="Doorkeeper", error="invalid_client", error_description="Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
> Cf-Cache-Status: DYNAMIC
> Server: cloudflare
> Cache-Control: no-store
> X-Download-Options: noopen
> X-Frame-Options: SAMEORIGIN
> X-Gitlab-Meta: {"correlation_id":"01HZ491CCGAH2VB7AC4JB34V9P","version":"1"}
> Gitlab-Lb: haproxy-main-50-lb-gprd
> Gitlab-Sv: web-gke-us-east1-d
> Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=AyFAiSgvHeQibif2jWObbbpAEbr4IShSNonhMsU6aFonp8WhnGrQjpiuB24ZP1jrJ9WzioZxI71YWH1joouXwDpqFqS4bos%2FEOGKlo7cCFH%2BClMrQJU0Dn0ubHs%3D"}],"group":"cf-nel","max_age":604800}
> Set-Cookie: _cfuvid=gGPOP3vi.ezz1OSuEf1PeJJ70YcFLNWGGyGKMKA05PE-1717056877089-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None

{
    "error": "invalid_client",
    "error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultDebug = func() DebugInterface {
	return &Debug{
		Writer: os.Stderr,
		Trace:  true,
		TraceCallback: func(w io.Writer, info TraceInfo) {
			_, _ = w.Write(info.Table())
		},
	}
}

Functions

func CodecForRequest added in v1.0.1

func CodecForRequest(r *http.Request, name ...string) (encoding.Codec, bool)

CodecForRequest get encoding.Codec via http.Request

func CodecForResponse added in v1.0.1

func CodecForResponse(r *http.Response, name ...string) (encoding.Codec, bool)

CodecForResponse get encoding.Codec via http.Response

func ContentSubtype

func ContentSubtype(contentType string) string

func ForceHttps

func ForceHttps(endpoint string) string

func FullPath

func FullPath(endpoint, path string) string

func GetCodecByContentType added in v1.0.5

func GetCodecByContentType(contentType string) encoding.Codec

func IsHTTPNot2xxError added in v1.0.6

func IsHTTPNot2xxError(err error) bool

func Not2xxCode

func Not2xxCode(code int) bool

func ProxyURL added in v1.0.1

func ProxyURL(address string) func(*http.Request) (*url.URL, error)

func RegisterCodecByContentType added in v1.0.5

func RegisterCodecByContentType(contentType string, codec encoding.Codec)

func RegisterCodecNameByContentType added in v1.1.0

func RegisterCodecNameByContentType(contentType string, name string)

Types

type CallOption

type CallOption interface {
	Before(request *http.Request) error
	After(response *http.Response) error
}

type CallOptions

type CallOptions struct {
	// request
	Query any // set query

	// Auth
	Username string // Basic Auth
	Password string

	BearerToken string // Bearer Token

	// hooks
	BeforeHook func(request *http.Request) error
	AfterHook  func(response *http.Response) error
}

func (*CallOptions) After

func (c *CallOptions) After(response *http.Response) error

func (*CallOptions) Before

func (c *CallOptions) Before(request *http.Request) error

type Client

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

Client is an HTTP client.

func NewClient

func NewClient(opts ...ClientOption) *Client

func (*Client) BindResponseBody added in v1.0.1

func (c *Client) BindResponseBody(response *http.Response, reply any) error

func (*Client) Do added in v1.0.1

func (c *Client) Do(req *http.Request, opts ...CallOption) (*http.Response, error)

Do send an HTTP request and decodes the body of response into target.

func (*Client) Endpoint

func (c *Client) Endpoint() string

func (*Client) Invoke

func (c *Client) Invoke(ctx context.Context, method, path string, args any, reply any, opts ...CallOption) (*http.Response, error)

Invoke makes a rpc call procedure for remote service.

func (*Client) SetEndpoint

func (c *Client) SetEndpoint(endpoint string)

type ClientOption

type ClientOption func(*clientOptions)

ClientOption is HTTP client option.

func WithContentType

func WithContentType(contentType string) ClientOption

WithContentType with client request content type.

func WithDebug

func WithDebug(f func() DebugInterface) ClientOption

WithDebug debug options

func WithEndpoint

func WithEndpoint(endpoint string) ClientOption

WithEndpoint with client addr.

func WithNot2xxError

func WithNot2xxError(f func() Not2xxError) ClientOption

WithNot2xxError handle response status code < 200 and code > 299

func WithProxy

func WithProxy(f func(*http.Request) (*url.URL, error)) ClientOption

WithProxy with proxy url.

func WithTLSConfig

func WithTLSConfig(cfg *tls.Config) ClientOption

WithTLSConfig with tls config.

func WithTimeout

func WithTimeout(timeout time.Duration) ClientOption

WithTimeout with client request timeout.

func WithTransport added in v1.0.1

func WithTransport(transport http.RoundTripper) ClientOption

WithTransport with http.RoundTrippe.

func WithUserAgent

func WithUserAgent(userAgent string) ClientOption

WithUserAgent with client user agent.

type Debug added in v1.0.1

type Debug struct {
	Writer        io.Writer
	Trace         bool
	TraceCallback func(w io.Writer, info TraceInfo)
	// contains filtered or unexported fields
}

func (*Debug) After added in v1.0.1

func (d *Debug) After(request *http.Request, response *http.Response)

func (*Debug) Before added in v1.0.1

func (d *Debug) Before() *httptrace.ClientTrace

type DebugInterface added in v1.0.1

type DebugInterface interface {
	Before() *httptrace.ClientTrace
	After(request *http.Request, response *http.Response)
}

type HTTPNot2xxError added in v1.0.6

type HTTPNot2xxError struct {
	URL        *url.URL
	Method     string
	StatusCode int
	Err        Not2xxError
}

func ConvertToHTTPNot2xxError added in v1.0.6

func ConvertToHTTPNot2xxError(err error) (*HTTPNot2xxError, bool)

func (HTTPNot2xxError) Error added in v1.0.6

func (h HTTPNot2xxError) Error() string

type Not2xxError

type Not2xxError interface {
	String() string
}

type TraceInfo added in v1.0.1

type TraceInfo struct {
	DNSDuration          time.Duration `json:"DNSDuration,omitempty" yaml:"DNSDuration" xml:"DNSDuration"`
	ConnectDuration      time.Duration `json:"connectDuration,omitempty" yaml:"connectDuration" xml:"connectDuration"`
	TLSHandshakeDuration time.Duration `json:"TLSHandshakeDuration,omitempty" yaml:"TLSHandshakeDuration" xml:"TLSHandshakeDuration"`
	RequestDuration      time.Duration `json:"requestDuration,omitempty" yaml:"requestDuration" xml:"requestDuration"`
	WaitResponseDuration time.Duration `json:"waitResponseDuration,omitempty" yaml:"waitResponseDuration" xml:"waitResponseDuration"`
	ResponseDuration     time.Duration `json:"responseDuration,omitempty" yaml:"responseDuration" xml:"responseDuration"`
	TotalDuration        time.Duration `json:"totalDuration,omitempty" yaml:"totalDuration" xml:"totalDuration"`
	// contains filtered or unexported fields
}

func (TraceInfo) Context added in v1.0.4

func (t TraceInfo) Context() context.Context

func (TraceInfo) String added in v1.0.1

func (t TraceInfo) String() string

func (TraceInfo) Table added in v1.0.1

func (t TraceInfo) Table() []byte

Directories

Path Synopsis
xml
examples

Jump to

Keyboard shortcuts

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