Documentation
¶
Index ¶
- Constants
- Variables
- func NewContext(ctx context.Context, v *Request) context.Context
- func ValuesOf(v any) (url.Values, error)
- type DebugOptions
- type Extension
- func (e Extension) Decode(ctx context.Context, body []byte, out any) error
- func (e Extension) Encode(ctx context.Context, body any) ([]byte, error)
- func (e Extension) HandleOption(r *Request, o any) (bool, error)
- func (e Extension) OnRequest(r *http.Request) error
- func (e Extension) OnResponse(r *http.Response) error
- func (e Extension) RoundTrip(r *http.Request) (*http.Response, error)
- func (e *Extension) With(h ...Hook)
- type Hook
- type MultipartFile
- type Request
- func (r Request) Do() (*http.Response, error)
- func (r Request) Fetch(out ...any) error
- func (r Request) FetchBytes() ([]byte, *http.Response, error)
- func (r Request) FetchString() (string, *http.Response, error)
- func (r Request) NewRequest() (*http.Request, error)
- func (r *Request) Reconcile() error
- func (r Request) With(o Request) Request
- func (r Request) WithHook(h ...Hook) Request
Examples ¶
Constants ¶
const RequestContextKey = contextKey("Request")
RequestContextKey for Request instance
Variables ¶
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 any) ([]byte, error) { v, err := ValuesOf(body) if err != nil { return nil, err } return []byte(v.Encode()), nil }, }
FormEncode encode use ValuesOf
var JSONDecode = Hook{ Name: "JsonDecode", Decode: func(ctx context.Context, body []byte, out any) error { return json.Unmarshal(body, out) }, }
JSONDecode decode use json.Unmarshal
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 any) ([]byte, error) { return json.Marshal(body) }, }
JSONEncode encode use json.Marshal, add Content-Type
var MultipartFormEncode = Hook{ Name: "MultipartFormEncode", Encode: func(ctx context.Context, body any) ([]byte, error) { buf := &bytes.Buffer{} writer := multipart.NewWriter(buf) var fieldName, filename string var reader io.Reader var fields map[string]string switch f := body.(type) { case *MultipartFile: fieldName = f.FieldName filename = f.Filename reader = f.Reader fields = f.Fields case fs.File: info, err := f.Stat() if err != nil { return nil, fmt.Errorf("stat file: %w", err) } filename = info.Name() reader = f default: return nil, fmt.Errorf("MultipartFormEncode: unsupported body type %T, use *MultipartFile or fs.File", body) } if fieldName == "" { fieldName = "file" } if filename == "" { filename = "upload" } for k, v := range fields { if err := writer.WriteField(k, v); err != nil { return nil, fmt.Errorf("write field %s: %w", k, err) } } part, err := writer.CreateFormFile(fieldName, filename) if err != nil { return nil, fmt.Errorf("create form file: %w", err) } if _, err := io.Copy(part, reader); err != nil { return nil, fmt.Errorf("copy file content: %w", err) } if err := writer.Close(); err != nil { return nil, fmt.Errorf("close multipart writer: %w", err) } return buf.Bytes(), nil }, OnRequest: func(r *http.Request) error { if r.Body != nil && r.Header.Get("Content-Type") == "" { body, err := io.ReadAll(r.Body) if err != nil { return err } if bytes.HasPrefix(body, []byte("--")) { idx := bytes.IndexByte(body, '\r') if idx < 0 { idx = bytes.IndexByte(body, '\n') } if idx > 2 { boundary := string(body[2:idx]) r.Header.Set("Content-Type", "multipart/form-data; boundary="+boundary) } } r.Body = io.NopCloser(bytes.NewReader(body)) r.ContentLength = int64(len(body)) } return nil }, }
MultipartFormEncode encodes a MultipartFile or fs.File as multipart/form-data. Body must be *MultipartFile or fs.File.
Functions ¶
func NewContext ¶
NewContext with Request
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) HandleOption ¶
HandleOption process unknown options
func (Extension) OnResponse ¶
OnResponse process response
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 any) (bool, error)
Encode func(ctx context.Context, body any) ([]byte, error)
Decode func(ctx context.Context, body []byte, out any) error
}
Hook phases for Extension
func UseRoundTripper ¶
func UseRoundTripper(rt http.RoundTripper) Hook
UseRoundTripper use customized http.RoundTripper for Request
type MultipartFile ¶ added in v0.11.0
type MultipartFile struct {
// FieldName is the form field name (default: "file")
FieldName string
// Filename is the filename to use in the multipart header
Filename string
// Reader provides the file content
Reader io.Reader
// Fields are extra form fields to include
Fields map[string]string
}
MultipartFile represents a file to be uploaded via multipart form
type Request ¶
type Request struct {
Method string
BaseURL string
URL string
Query any
RawQuery string
RawBody []byte
GetBody func() (io.ReadCloser, error)
Body any
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 []any
Extension Extension
}
Request is declarative HTTP client instance
Example ¶
// reusable client
client := req.Request{
BaseURL: "https://httpbin.org",
Options: []any{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: []any{req.FormEncode},
}).Fetch(&out)
if err != nil {
panic(err)
}
// print go-req
fmt.Println(out.Form.Name)
func FromContext ¶
FromContext get Request from context.Context
func (Request) FetchBytes ¶
FetchBytes return bytes
func (Request) FetchString ¶
FetchString return string
func (Request) NewRequest ¶
NewRequest create http.Request