req

package module
v0.10.1 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2022 License: MIT Imports: 18 Imported by: 6

README

go-req

GoDoc Build Status Coverage Status Go Report Card

Declarative golang HTTP client

package req_test

import (
    "fmt"
    "github.com/wenerme/go-req"
    "net/http"
)

func ExampleRequest() {
    // reusable client
    client := req.Request{
        BaseURL: "https://httpbin.org",
        Options: []interface{}{req.JSONEncode, req.JSONDecode},
    }
  
    // dump request and response with body
    client = client.WithHook(req.DebugHook(&req.DebugOptions{Body: true}))
    
    // send request with declarative override
    var out PostResponse
    var r *http.Response
    err := client.With(req.Request{
        Method: http.MethodPost,
        URL: "/post",
        Body: HelloRequest{
          Name: "go-req",
        },
    }).Fetch(&out,&r)
    if err != nil {
      panic(err)
    }
    // print go-req
    fmt.Println(out.JSON.Name)
    // print 200
    fmt.Println(r.StatusCode)
}

type HelloRequest struct {
  Name string
}
type HelloResponse struct {
  Name string
}
type PostResponse struct {
  JSON HelloResponse `json:"json"`
}

Used by

Documentation

Index

Examples

Constants

View Source
const RequestContextKey = contextKey("Request")

RequestContextKey for Request instance

Variables

View Source
var FormEncode = Hook{
	Name: "FormEncode",
	OnRequest: func(r *http.Request) error {
		if r.Header.Get("Content-Type") == "" {
			r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
		return nil
	},
	Encode: func(ctx context.Context, body interface{}) ([]byte, error) {
		v, err := ValuesOf(body)
		if err != nil {
			return nil, err
		}
		return []byte(v.Encode()), nil
	},
}

FormEncode encode use ValuesOf

View Source
var JSONDecode = Hook{
	Name: "JsonDecode",
	Decode: func(ctx context.Context, body []byte, out interface{}) error {
		return json.Unmarshal(body, out)
	},
}

JSONDecode decode use json.Unmarshal

View Source
var JSONEncode = Hook{
	Name: "JsonEncode",
	OnRequest: func(r *http.Request) error {
		if r.Header.Get("Content-Type") == "" {
			r.Header.Set("Content-Type", "application/json;charset=UTF-8")
		}
		return nil
	},
	Encode: func(ctx context.Context, body interface{}) ([]byte, error) {
		return json.Marshal(body)
	},
}

JSONEncode encode use json.Marshal, add Content-Type

View Source
var MultipartFormEncode = Hook{
	Name: "MultipartFormEncode",
	OnRequest: func(r *http.Request) (err error) {
		if r.Body == nil {
			return errors.New("MultipartFormEncode need file body")
		}
		h := multipart.FileHeader{}
		switch f := r.Body.(type) {

		case fs.File:
			info, err := f.Stat()
			if err != nil {
				return err
			}
			h.Filename = info.Name()
			h.Size = info.Size()
		default:
			return errors.Errorf("unsupported file type: %T", r.Body)
		}
		r.GetBody = func() (io.ReadCloser, error) {
			b := &bytes.Buffer{}

			return io.NopCloser(b), nil
		}
		return
	},
}

Functions

func NewContext

func NewContext(ctx context.Context, v *Request) context.Context

NewContext with Request

func ValuesOf

func ValuesOf(v interface{}) (url.Values, error)

ValuesOf convert anything to url.Values

Types

type DebugOptions

type DebugOptions struct {
	Disable   bool                        // Disable turn off debug
	Body      bool                        // Body enable dump http request and response's body
	Out       io.Writer                   // Out debug output, default stderr
	ErrorOnly bool                        // ErrorOnly enable dump error only
	IsError   func(r *http.Response) bool // IsError check if response is error, default is http.StatusOK < 400
}

DebugOptions options for DebugHook

type Extension

type Extension struct {
	Hooks []Hook
}

Extension of Request

func (Extension) Decode

func (e Extension) Decode(ctx context.Context, body []byte, out interface{}) error

Decode body

func (Extension) Encode

func (e Extension) Encode(ctx context.Context, body interface{}) ([]byte, error)

Encode body

func (Extension) HandleOption

func (e Extension) HandleOption(r *Request, o interface{}) (bool, error)

HandleOption process unknown options

func (Extension) OnRequest

func (e Extension) OnRequest(r *http.Request) error

OnRequest process request

func (Extension) OnResponse

func (e Extension) OnResponse(r *http.Response) error

OnResponse process response

func (Extension) RoundTrip

func (e Extension) RoundTrip(r *http.Request) (*http.Response, error)

RoundTrip process request to response

func (*Extension) With

func (e *Extension) With(h ...Hook)

With more hooks

type Hook

type Hook struct {
	Name          string
	Order         int
	OnRequest     func(r *http.Request) error
	OnResponse    func(r *http.Response) error
	HandleRequest func(next http.RoundTripper) http.RoundTripper
	HandleOption  func(r *Request, o interface{}) (bool, error)
	Encode        func(ctx context.Context, body interface{}) ([]byte, error)
	Decode        func(ctx context.Context, body []byte, out interface{}) error
}

Hook phases for Extension

func DebugHook

func DebugHook(o *DebugOptions) Hook

DebugHook dump http.Request and http.Response

func UseRoundTripper

func UseRoundTripper(rt http.RoundTripper) Hook

UseRoundTripper use customized http.RoundTripper for Request

type Request

type Request struct {
	Method   string
	BaseURL  string
	URL      string
	Query    interface{}
	RawQuery string
	RawBody  []byte
	GetBody  func() (io.ReadCloser, error)
	Body     interface{}
	Header   http.Header
	Context  context.Context

	Values    url.Values // Extra options for customized process - non string option use Context
	LastError error
	// Options support signatures
	//   Request
	//   func(*Request)
	//   func(*Request) error
	//   Hook
	//   nil
	Options   []interface{}
	Extension Extension
}

Request is declarative HTTP client instance

Example
// reusable client
client := req.Request{
	BaseURL: "https://httpbin.org",
	Options: []interface{}{req.JSONEncode, req.JSONDecode},
}

// dump request and response with body
client = client.WithHook(req.DebugHook(&req.DebugOptions{Body: true}))

// send request with declarative override
var out PostResponse
var r *http.Response
err := client.With(req.Request{
	Method: http.MethodPost,
	URL:    "/post",
	Body: HelloRequest{
		Name: "go-req",
	},
}).Fetch(&out, &r)
if err != nil {
	panic(err)
}
// print go-req
fmt.Println(out.JSON.Name)
// print 200
fmt.Println(r.StatusCode)

// override Options use form encode
err = client.With(req.Request{
	Method: http.MethodPost,
	URL:    "/post",
	Body: HelloRequest{
		Name: "go-req",
	},
	Options: []interface{}{req.FormEncode},
}).Fetch(&out)
if err != nil {
	panic(err)
}
// print go-req
fmt.Println(out.Form.Name)
Output:

func FromContext

func FromContext(ctx context.Context) *Request

FromContext get Request from context.Context

func (Request) Do

func (r Request) Do() (*http.Response, error)

Do Request

func (Request) Fetch

func (r Request) Fetch(out ...interface{}) error

Fetch decode body

func (Request) FetchBytes

func (r Request) FetchBytes() ([]byte, *http.Response, error)

FetchBytes return bytes

func (Request) FetchString

func (r Request) FetchString() (string, *http.Response, error)

FetchString return string

func (Request) NewRequest

func (r Request) NewRequest() (*http.Request, error)

NewRequest create http.Request

func (*Request) Reconcile

func (r *Request) Reconcile() error

Reconcile apply current options, de-sugar request

func (Request) With

func (r Request) With(o Request) Request

With override Request

func (Request) WithHook

func (r Request) WithHook(h ...Hook) Request

WithHook add Hook to Extension

Jump to

Keyboard shortcuts

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