mirror of https://github.com/gofiber/fiber.git
🔥 feat: Add support for iterator methods to Fiber client (#3228)
* chore: simplify parserRequestBodyFile logic * client: add support for go1.23 iterators * Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix linter * fix tests * correct benchmark * fix linter * create docs * update * rename FormDatas -> AllFormData * add examples for maps.Collect() * change request/response markdown examples --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: René <rene@gofiber.io>pull/3243/head
parent
27cfd3c8cd
commit
e9849b758d
2
Makefile
2
Makefile
|
@ -27,7 +27,7 @@ coverage:
|
|||
format:
|
||||
go run mvdan.cc/gofumpt@latest -w -l .
|
||||
|
||||
## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli)
|
||||
## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli2)
|
||||
.PHONY: markdown
|
||||
markdown:
|
||||
markdownlint-cli2 "**/*.md" "#vendor"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
@ -241,8 +240,8 @@ func parserRequestBodyFile(req *Request) error {
|
|||
return fmt.Errorf("write formdata error: %w", err)
|
||||
}
|
||||
|
||||
// add file
|
||||
b := make([]byte, 512)
|
||||
// add files
|
||||
fileBuf := make([]byte, 1<<20) // Allocate 1MB buffer
|
||||
for i, v := range req.files {
|
||||
if v.name == "" && v.path == "" {
|
||||
return ErrFileNoName
|
||||
|
@ -273,24 +272,12 @@ func parserRequestBodyFile(req *Request) error {
|
|||
return fmt.Errorf("create file error: %w", err)
|
||||
}
|
||||
|
||||
for {
|
||||
n, err := v.reader.Read(b)
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return fmt.Errorf("read file error: %w", err)
|
||||
}
|
||||
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
|
||||
_, err = w.Write(b[:n])
|
||||
if err != nil {
|
||||
return fmt.Errorf("write file error: %w", err)
|
||||
}
|
||||
// Copy the file from reader to multipart writer
|
||||
if _, err := io.CopyBuffer(w, v.reader, fileBuf); err != nil {
|
||||
return fmt.Errorf("failed to copy file data: %w", err)
|
||||
}
|
||||
|
||||
err = v.reader.Close()
|
||||
if err != nil {
|
||||
if err := v.reader.Close(); err != nil {
|
||||
return fmt.Errorf("close file error: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"iter"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -129,6 +131,31 @@ func (r *Request) Header(key string) []string {
|
|||
return r.header.PeekMultiple(key)
|
||||
}
|
||||
|
||||
// Headers returns all headers in the request using an iterator.
|
||||
// You can use maps.Collect() to collect all headers into a map.
|
||||
//
|
||||
// The returned value is valid until the request object is released.
|
||||
// Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
func (r *Request) Headers() iter.Seq2[string, []string] {
|
||||
return func(yield func(string, []string) bool) {
|
||||
peekKeys := r.header.PeekKeys()
|
||||
keys := make([][]byte, len(peekKeys))
|
||||
copy(keys, peekKeys) // It is necessary to have immutable byte slice.
|
||||
|
||||
for _, key := range keys {
|
||||
vals := r.header.PeekAll(utils.UnsafeString(key))
|
||||
valsStr := make([]string, len(vals))
|
||||
for i, v := range vals {
|
||||
valsStr[i] = utils.UnsafeString(v)
|
||||
}
|
||||
|
||||
if !yield(utils.UnsafeString(key), valsStr) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddHeader method adds a single header field and its value in the request instance.
|
||||
func (r *Request) AddHeader(key, val string) *Request {
|
||||
r.header.Add(key, val)
|
||||
|
@ -168,6 +195,33 @@ func (r *Request) Param(key string) []string {
|
|||
return res
|
||||
}
|
||||
|
||||
// Params returns all params in the request using an iterator.
|
||||
// You can use maps.Collect() to collect all params into a map.
|
||||
//
|
||||
// The returned value is valid until the request object is released.
|
||||
// Any future calls to Params method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
func (r *Request) Params() iter.Seq2[string, []string] {
|
||||
return func(yield func(string, []string) bool) {
|
||||
keys := r.params.Keys()
|
||||
|
||||
for _, key := range keys {
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
vals := r.params.PeekMulti(key)
|
||||
valsStr := make([]string, len(vals))
|
||||
for i, v := range vals {
|
||||
valsStr[i] = utils.UnsafeString(v)
|
||||
}
|
||||
|
||||
if !yield(key, valsStr) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddParam method adds a single param field and its value in the request instance.
|
||||
func (r *Request) AddParam(key, val string) *Request {
|
||||
r.params.Add(key, val)
|
||||
|
@ -254,6 +308,18 @@ func (r *Request) Cookie(key string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// Cookies returns all cookies in the cookies using an iterator.
|
||||
// You can use maps.Collect() to collect all cookies into a map.
|
||||
func (r *Request) Cookies() iter.Seq2[string, string] {
|
||||
return func(yield func(string, string) bool) {
|
||||
r.cookies.VisitAll(func(key, val string) {
|
||||
if !yield(key, val) {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// SetCookie method sets a single cookie field and its value in the request instance.
|
||||
// It will override cookie which set in client instance.
|
||||
func (r *Request) SetCookie(key, val string) *Request {
|
||||
|
@ -291,6 +357,18 @@ func (r *Request) PathParam(key string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// PathParams returns all path params in request instance.
|
||||
// You can use maps.Collect() to collect all cookies into a map.
|
||||
func (r *Request) PathParams() iter.Seq2[string, string] {
|
||||
return func(yield func(string, string) bool) {
|
||||
r.path.VisitAll(func(key, val string) {
|
||||
if !yield(key, val) {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// SetPathParam method sets a single path param field and its value in the request instance.
|
||||
// It will override path param which set in client instance.
|
||||
func (r *Request) SetPathParam(key, val string) *Request {
|
||||
|
@ -376,6 +454,33 @@ func (r *Request) FormData(key string) []string {
|
|||
return res
|
||||
}
|
||||
|
||||
// AllFormData method returns all form datas in request instance.
|
||||
// You can use maps.Collect() to collect all cookies into a map.
|
||||
//
|
||||
// The returned value is valid until the request object is released.
|
||||
// Any future calls to FormDatas method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
func (r *Request) AllFormData() iter.Seq2[string, []string] {
|
||||
return func(yield func(string, []string) bool) {
|
||||
keys := r.formData.Keys()
|
||||
|
||||
for _, key := range keys {
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
vals := r.formData.PeekMulti(key)
|
||||
valsStr := make([]string, len(vals))
|
||||
for i, v := range vals {
|
||||
valsStr[i] = utils.UnsafeString(v)
|
||||
}
|
||||
|
||||
if !yield(key, valsStr) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddFormData method adds a single form data field and its value in the request instance.
|
||||
func (r *Request) AddFormData(key, val string) *Request {
|
||||
r.formData.AddData(key, val)
|
||||
|
@ -435,6 +540,14 @@ func (r *Request) File(name string) *File {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Files method returns all files in request instance.
|
||||
//
|
||||
// The returned value is valid until the request object is released.
|
||||
// Any future calls to Files method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
func (r *Request) Files() []*File {
|
||||
return r.files
|
||||
}
|
||||
|
||||
// FileByPath returns file ptr store in request obj by path.
|
||||
func (r *Request) FileByPath(path string) *File {
|
||||
for _, v := range r.files {
|
||||
|
@ -617,6 +730,16 @@ type QueryParam struct {
|
|||
*fasthttp.Args
|
||||
}
|
||||
|
||||
// Keys method returns all keys in the query params.
|
||||
func (p *QueryParam) Keys() []string {
|
||||
keys := make([]string, 0, p.Len())
|
||||
p.VisitAll(func(key, _ []byte) {
|
||||
keys = append(keys, utils.UnsafeString(key))
|
||||
})
|
||||
|
||||
return slices.Compact(keys)
|
||||
}
|
||||
|
||||
// AddParams receive a map and add each value to param.
|
||||
func (p *QueryParam) AddParams(r map[string][]string) {
|
||||
for k, v := range r {
|
||||
|
@ -747,6 +870,16 @@ type FormData struct {
|
|||
*fasthttp.Args
|
||||
}
|
||||
|
||||
// Keys method returns all keys in the form data.
|
||||
func (f *FormData) Keys() []string {
|
||||
keys := make([]string, 0, f.Len())
|
||||
f.VisitAll(func(key, _ []byte) {
|
||||
keys = append(keys, utils.UnsafeString(key))
|
||||
})
|
||||
|
||||
return slices.Compact(keys)
|
||||
}
|
||||
|
||||
// AddData method is a wrapper of Args's Add method.
|
||||
func (f *FormData) AddData(key, val string) {
|
||||
f.Add(key, val)
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"maps"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -157,6 +158,42 @@ func Test_Request_Header(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func Test_Request_Headers(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := AcquireRequest()
|
||||
req.AddHeaders(map[string][]string{
|
||||
"foo": {"bar", "fiber"},
|
||||
"bar": {"foo"},
|
||||
})
|
||||
|
||||
headers := maps.Collect(req.Headers())
|
||||
|
||||
require.Contains(t, headers["Foo"], "fiber")
|
||||
require.Contains(t, headers["Foo"], "bar")
|
||||
require.Contains(t, headers["Bar"], "foo")
|
||||
|
||||
require.Len(t, headers, 2)
|
||||
}
|
||||
|
||||
func Benchmark_Request_Headers(b *testing.B) {
|
||||
req := AcquireRequest()
|
||||
req.AddHeaders(map[string][]string{
|
||||
"foo": {"bar", "fiber"},
|
||||
"bar": {"foo"},
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k, v := range req.Headers() {
|
||||
_ = k
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Request_QueryParam(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -282,6 +319,42 @@ func Test_Request_QueryParam(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func Test_Request_Params(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := AcquireRequest()
|
||||
req.AddParams(map[string][]string{
|
||||
"foo": {"bar", "fiber"},
|
||||
"bar": {"foo"},
|
||||
})
|
||||
|
||||
pathParams := maps.Collect(req.Params())
|
||||
|
||||
require.Contains(t, pathParams["foo"], "bar")
|
||||
require.Contains(t, pathParams["foo"], "fiber")
|
||||
require.Contains(t, pathParams["bar"], "foo")
|
||||
|
||||
require.Len(t, pathParams, 2)
|
||||
}
|
||||
|
||||
func Benchmark_Request_Params(b *testing.B) {
|
||||
req := AcquireRequest()
|
||||
req.AddParams(map[string][]string{
|
||||
"foo": {"bar", "fiber"},
|
||||
"bar": {"foo"},
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k, v := range req.Params() {
|
||||
_ = k
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Request_UA(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -364,6 +437,41 @@ func Test_Request_Cookie(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func Test_Request_Cookies(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := AcquireRequest()
|
||||
req.SetCookies(map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "foo",
|
||||
})
|
||||
|
||||
cookies := maps.Collect(req.Cookies())
|
||||
|
||||
require.Equal(t, "bar", cookies["foo"])
|
||||
require.Equal(t, "foo", cookies["bar"])
|
||||
|
||||
require.Len(t, cookies, 2)
|
||||
}
|
||||
|
||||
func Benchmark_Request_Cookies(b *testing.B) {
|
||||
req := AcquireRequest()
|
||||
req.SetCookies(map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "foo",
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k, v := range req.Cookies() {
|
||||
_ = k
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Request_PathParam(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -441,6 +549,41 @@ func Test_Request_PathParam(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func Test_Request_PathParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := AcquireRequest()
|
||||
req.SetPathParams(map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "foo",
|
||||
})
|
||||
|
||||
pathParams := maps.Collect(req.PathParams())
|
||||
|
||||
require.Equal(t, "bar", pathParams["foo"])
|
||||
require.Equal(t, "foo", pathParams["bar"])
|
||||
|
||||
require.Len(t, pathParams, 2)
|
||||
}
|
||||
|
||||
func Benchmark_Request_PathParams(b *testing.B) {
|
||||
req := AcquireRequest()
|
||||
req.SetPathParams(map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "foo",
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k, v := range req.PathParams() {
|
||||
_ = k
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Request_FormData(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -610,6 +753,40 @@ func Test_Request_File(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func Test_Request_Files(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := AcquireRequest()
|
||||
req.AddFile("../.github/index.html")
|
||||
req.AddFiles(AcquireFile(SetFileName("tmp.txt")))
|
||||
|
||||
files := req.Files()
|
||||
|
||||
require.Equal(t, "../.github/index.html", files[0].path)
|
||||
require.Nil(t, files[0].reader)
|
||||
|
||||
require.Equal(t, "tmp.txt", files[1].name)
|
||||
require.Nil(t, files[1].reader)
|
||||
|
||||
require.Len(t, files, 2)
|
||||
}
|
||||
|
||||
func Benchmark_Request_Files(b *testing.B) {
|
||||
req := AcquireRequest()
|
||||
req.AddFile("../.github/index.html")
|
||||
req.AddFiles(AcquireFile(SetFileName("tmp.txt")))
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k, v := range req.Files() {
|
||||
_ = k
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Request_Timeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1181,6 +1358,42 @@ func Test_Request_Body_With_Server(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func Test_Request_AllFormData(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := AcquireRequest()
|
||||
req.AddFormDatas(map[string][]string{
|
||||
"foo": {"bar", "fiber"},
|
||||
"bar": {"foo"},
|
||||
})
|
||||
|
||||
pathParams := maps.Collect(req.AllFormData())
|
||||
|
||||
require.Contains(t, pathParams["foo"], "bar")
|
||||
require.Contains(t, pathParams["foo"], "fiber")
|
||||
require.Contains(t, pathParams["bar"], "foo")
|
||||
|
||||
require.Len(t, pathParams, 2)
|
||||
}
|
||||
|
||||
func Benchmark_Request_AllFormData(b *testing.B) {
|
||||
req := AcquireRequest()
|
||||
req.AddFormDatas(map[string][]string{
|
||||
"foo": {"bar", "fiber"},
|
||||
"bar": {"foo"},
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k, v := range req.AllFormData() {
|
||||
_ = k
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Request_Error_Body_With_Server(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("json error", func(t *testing.T) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"iter"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
@ -55,7 +56,33 @@ func (r *Response) Header(key string) string {
|
|||
return utils.UnsafeString(r.RawResponse.Header.Peek(key))
|
||||
}
|
||||
|
||||
// Headers returns all headers in the response using an iterator.
|
||||
// You can use maps.Collect() to collect all headers into a map.
|
||||
//
|
||||
// The returned value is valid until the response object is released.
|
||||
// Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
func (r *Response) Headers() iter.Seq2[string, []string] {
|
||||
return func(yield func(string, []string) bool) {
|
||||
keys := r.RawResponse.Header.PeekKeys()
|
||||
|
||||
for _, key := range keys {
|
||||
vals := r.RawResponse.Header.PeekAll(utils.UnsafeString(key))
|
||||
valsStr := make([]string, len(vals))
|
||||
for i, v := range vals {
|
||||
valsStr[i] = utils.UnsafeString(v)
|
||||
}
|
||||
|
||||
if !yield(utils.UnsafeString(key), valsStr) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cookies method to access all the response cookies.
|
||||
//
|
||||
// The returned value is valid until the response object is released.
|
||||
// Any future calls to Cookies method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
func (r *Response) Cookies() []*fasthttp.Cookie {
|
||||
return r.cookie
|
||||
}
|
||||
|
|
|
@ -199,6 +199,85 @@ func Test_Response_Header(t *testing.T) {
|
|||
resp.Close()
|
||||
}
|
||||
|
||||
func Test_Response_Headers(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := startTestServer(t, func(app *fiber.App) {
|
||||
app.Get("/", func(c fiber.Ctx) error {
|
||||
c.Response().Header.Add("foo", "bar")
|
||||
c.Response().Header.Add("foo", "bar2")
|
||||
c.Response().Header.Add("foo2", "bar")
|
||||
|
||||
return c.SendString("hello world")
|
||||
})
|
||||
})
|
||||
defer server.stop()
|
||||
|
||||
client := New().SetDial(server.dial())
|
||||
|
||||
resp, err := AcquireRequest().
|
||||
SetClient(client).
|
||||
Get("http://example.com")
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
headers := make(map[string][]string)
|
||||
for k, v := range resp.Headers() {
|
||||
headers[k] = append(headers[k], v...)
|
||||
}
|
||||
|
||||
require.Equal(t, "hello world", resp.String())
|
||||
|
||||
require.Contains(t, headers["Foo"], "bar")
|
||||
require.Contains(t, headers["Foo"], "bar2")
|
||||
require.Contains(t, headers["Foo2"], "bar")
|
||||
|
||||
require.Len(t, headers, 3) // Foo + Foo2 + Date
|
||||
|
||||
resp.Close()
|
||||
}
|
||||
|
||||
func Benchmark_Headers(b *testing.B) {
|
||||
server := startTestServer(
|
||||
b,
|
||||
func(app *fiber.App) {
|
||||
app.Get("/", func(c fiber.Ctx) error {
|
||||
c.Response().Header.Add("foo", "bar")
|
||||
c.Response().Header.Add("foo", "bar2")
|
||||
c.Response().Header.Add("foo", "bar3")
|
||||
|
||||
c.Response().Header.Add("foo2", "bar")
|
||||
c.Response().Header.Add("foo2", "bar2")
|
||||
c.Response().Header.Add("foo2", "bar3")
|
||||
|
||||
return c.SendString("helo world")
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
client := New().SetDial(server.dial())
|
||||
|
||||
resp, err := AcquireRequest().
|
||||
SetClient(client).
|
||||
Get("http://example.com")
|
||||
require.NoError(b, err)
|
||||
|
||||
b.Cleanup(func() {
|
||||
resp.Close()
|
||||
server.stop()
|
||||
})
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for k, v := range resp.Headers() {
|
||||
_ = k
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Response_Cookie(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -211,6 +211,60 @@ Header method returns header value via key, this method will visit all field in
|
|||
func (r *Request) Header(key string) []string
|
||||
```
|
||||
|
||||
### Headers
|
||||
|
||||
Headers returns all headers in the request using an iterator. You can use `maps.Collect()` to collect all headers into a map.
|
||||
The returned value is valid until the request object is released. Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Request) Headers() iter.Seq2[string, []string]
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
|
||||
req.AddHeader("Golang", "Fiber")
|
||||
req.AddHeader("Test", "123456")
|
||||
req.AddHeader("Test", "654321")
|
||||
|
||||
for k, v := range req.Headers() {
|
||||
fmt.Printf("Header Key: %s, Header Value: %v\n", k, v)
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
Header Key: Golang, Header Value: [Fiber]
|
||||
Header Key: Test, Header Value: [123456 654321]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Example with maps.Collect()</summary>
|
||||
|
||||
```go title="Example with maps.Collect()"
|
||||
req := client.AcquireRequest()
|
||||
|
||||
req.AddHeader("Golang", "Fiber")
|
||||
req.AddHeader("Test", "123456")
|
||||
req.AddHeader("Test", "654321")
|
||||
|
||||
headers := maps.Collect(req.Headers()) // Collect all headers into a map
|
||||
for k, v := range headers {
|
||||
fmt.Printf("Header Key: %s, Header Value: %v\n", k, v)
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
Header Key: Golang, Header Value: [Fiber]
|
||||
Header Key: Test, Header Value: [123456 654321]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### AddHeader
|
||||
|
||||
AddHeader method adds a single header field and its value in the request instance.
|
||||
|
@ -219,6 +273,9 @@ AddHeader method adds a single header field and its value in the request instanc
|
|||
func (r *Request) AddHeader(key, val string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -235,9 +292,6 @@ if err != nil {
|
|||
fmt.Println(resp.String())
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"headers": {
|
||||
|
@ -262,6 +316,9 @@ It will override the header which has been set in the client instance.
|
|||
func (r *Request) SetHeader(key, val string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -277,9 +334,6 @@ if err != nil {
|
|||
fmt.Println(resp.String())
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"headers": {
|
||||
|
@ -320,6 +374,15 @@ Param method returns params value via key, this method will visit all field in t
|
|||
func (r *Request) Param(key string) []string
|
||||
```
|
||||
|
||||
### Params
|
||||
|
||||
Params returns all params in the request using an iterator. You can use `maps.Collect()` to collect all params into a map.
|
||||
The returned value is valid until the request object is released. Any future calls to Params method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Request) Params() iter.Seq2[string, []string]
|
||||
```
|
||||
|
||||
### AddParam
|
||||
|
||||
AddParam method adds a single param field and its value in the request instance.
|
||||
|
@ -328,6 +391,9 @@ AddParam method adds a single param field and its value in the request instance.
|
|||
func (r *Request) AddParam(key, val string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -344,9 +410,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"Content-Length": "145",
|
||||
|
@ -396,6 +459,9 @@ It will override param, which has been set in client instance.
|
|||
func (r *Request) SetParamsWithStruct(v any) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -419,9 +485,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"Content-Length": "147",
|
||||
|
@ -502,6 +565,14 @@ Cookie returns the cookie set in the request instance. If the cookie doesn't exi
|
|||
func (r *Request) Cookie(key string) string
|
||||
```
|
||||
|
||||
### Cookies
|
||||
|
||||
Cookies returns all cookies in the request using an iterator. You can use `maps.Collect()` to collect all cookies into a map.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Request) Cookies() iter.Seq2[string, string]
|
||||
```
|
||||
|
||||
### SetCookie
|
||||
|
||||
SetCookie method sets a single cookie field and its value in the request instance.
|
||||
|
@ -520,6 +591,9 @@ It will override the cookie which is set in the client instance.
|
|||
func (r *Request) SetCookies(m map[string]string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -537,9 +611,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"cookies": {
|
||||
|
@ -575,6 +646,14 @@ PathParam returns the path param set in the request instance. If the path param
|
|||
func (r *Request) PathParam(key string) string
|
||||
```
|
||||
|
||||
### PathParams
|
||||
|
||||
PathParams returns all path params in the request using an iterator. You can use `maps.Collect()` to collect all path params into a map.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Request) PathParams() iter.Seq2[string, string]
|
||||
```
|
||||
|
||||
### SetPathParam
|
||||
|
||||
SetPathParam method sets a single path param field and its value in the request instance.
|
||||
|
@ -584,6 +663,9 @@ It will override path param which set in client instance.
|
|||
func (r *Request) SetPathParam(key, val string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -598,9 +680,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```plaintext
|
||||
Gofiber
|
||||
```
|
||||
|
@ -682,6 +761,14 @@ FormData method returns form data value via key, this method will visit all fiel
|
|||
func (r *Request) FormData(key string) []string
|
||||
```
|
||||
|
||||
### AllFormData
|
||||
|
||||
AllFormData returns all form data in the request using an iterator. You can use `maps.Collect()` to collect all form data into a map.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Request) AllFormData() iter.Seq2[string, []string]
|
||||
```
|
||||
|
||||
### AddFormData
|
||||
|
||||
AddFormData method adds a single form data field and its value in the request instance.
|
||||
|
@ -690,6 +777,9 @@ AddFormData method adds a single form data field and its value in the request in
|
|||
func (r *Request) AddFormData(key, val string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -706,9 +796,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"args": {},
|
||||
|
@ -735,6 +822,9 @@ SetFormData method sets a single form data field and its value in the request in
|
|||
func (r *Request) SetFormData(key, val string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -750,9 +840,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"args": {},
|
||||
|
@ -817,6 +904,15 @@ If the name field is empty, it will try to match path.
|
|||
func (r *Request) File(name string) *File
|
||||
```
|
||||
|
||||
### Files
|
||||
|
||||
Files method returns all files in request instance.
|
||||
The returned value is valid until the request object is released. Any future calls to Files method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Request) Files() []*File
|
||||
```
|
||||
|
||||
### FileByPath
|
||||
|
||||
FileByPath returns file ptr store in request obj by path.
|
||||
|
@ -833,6 +929,9 @@ AddFile method adds a single file field and its value in the request instance vi
|
|||
func (r *Request) AddFile(path string) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -847,9 +946,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"args": {},
|
||||
|
@ -872,6 +968,9 @@ AddFileWithReader method adds a single field and its value in the request instan
|
|||
func (r *Request) AddFileWithReader(name string, reader io.ReadCloser) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -887,9 +986,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"args": {},
|
||||
|
@ -929,6 +1025,9 @@ It will override timeout which set in client instance.
|
|||
func (r *Request) SetTimeout(t time.Duration) *Request
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example 1</summary>
|
||||
|
||||
```go title="Example 1"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -943,9 +1042,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"args": {},
|
||||
|
@ -958,6 +1054,9 @@ fmt.Println(string(resp.Body()))
|
|||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Example 2</summary>
|
||||
|
||||
```go title="Example 2"
|
||||
req := client.AcquireRequest()
|
||||
defer client.ReleaseRequest(req)
|
||||
|
@ -972,9 +1071,6 @@ if err != nil {
|
|||
fmt.Println(string(resp.Body()))
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```shell
|
||||
panic: timeout or cancel
|
||||
|
||||
|
@ -1063,6 +1159,14 @@ type QueryParam struct {
|
|||
}
|
||||
```
|
||||
|
||||
### Keys
|
||||
|
||||
Keys method returns all keys in the query params.
|
||||
|
||||
```go title="Signature"
|
||||
func (p *QueryParam) Keys() []string
|
||||
```
|
||||
|
||||
### AddParams
|
||||
|
||||
AddParams receive a map and add each value to param.
|
||||
|
@ -1242,6 +1346,14 @@ type FormData struct {
|
|||
}
|
||||
```
|
||||
|
||||
### Keys
|
||||
|
||||
Keys method returns all keys in the form data.
|
||||
|
||||
```go title="Signature"
|
||||
func (f *FormData) Keys() []string
|
||||
```
|
||||
|
||||
### AddData
|
||||
|
||||
AddData method is a wrapper of Args's Add method.
|
||||
|
|
|
@ -68,6 +68,9 @@ Protocol method returns the HTTP response protocol used for the request.
|
|||
func (r *Response) Protocol() string
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
resp, err := client.Get("https://httpbin.org/get")
|
||||
if err != nil {
|
||||
|
@ -77,9 +80,6 @@ if err != nil {
|
|||
fmt.Println(resp.Protocol())
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```text
|
||||
HTTP/1.1
|
||||
```
|
||||
|
@ -94,14 +94,74 @@ Header method returns the response headers.
|
|||
func (r *Response) Header(key string) string
|
||||
```
|
||||
|
||||
## Headers
|
||||
|
||||
Headers returns all headers in the response using an iterator. You can use `maps.Collect()` to collect all headers into a map.
|
||||
The returned value is valid until the response object is released. Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Response) Headers() iter.Seq2[string, []string]
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
resp, err := client.Get("https://httpbin.org/get")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for key, values := range resp.Headers() {
|
||||
fmt.Printf("%s => %s\n", key, strings.Join(values, ", "))
|
||||
}
|
||||
```
|
||||
|
||||
```text
|
||||
Date => Wed, 04 Dec 2024 15:28:29 GMT
|
||||
Connection => keep-alive
|
||||
Access-Control-Allow-Origin => *
|
||||
Access-Control-Allow-Credentials => true
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Example with maps.Collect()</summary>
|
||||
|
||||
```go title="Example with maps.Collect()"
|
||||
resp, err := client.Get("https://httpbin.org/get")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
headers := maps.Collect(resp.Headers()) // Collect all headers into a map
|
||||
for key, values := range headers {
|
||||
fmt.Printf("%s => %s\n", key, strings.Join(values, ", "))
|
||||
}
|
||||
```
|
||||
|
||||
```text
|
||||
Date => Wed, 04 Dec 2024 15:28:29 GMT
|
||||
Connection => keep-alive
|
||||
Access-Control-Allow-Origin => *
|
||||
Access-Control-Allow-Credentials => true
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Cookies
|
||||
|
||||
Cookies method to access all the response cookies.
|
||||
The returned value is valid until the response object is released. Any future calls to Cookies method will return the modified value. Do not store references to returned value. Make copies instead.
|
||||
|
||||
```go title="Signature"
|
||||
func (r *Response) Cookies() []*fasthttp.Cookie
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
resp, err := client.Get("https://httpbin.org/cookies/set/go/fiber")
|
||||
if err != nil {
|
||||
|
@ -114,9 +174,6 @@ for _, cookie := range cookies {
|
|||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```text
|
||||
go => fiber
|
||||
```
|
||||
|
@ -147,6 +204,9 @@ JSON method will unmarshal body to json.
|
|||
func (r *Response) JSON(v any) error
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
```go title="Example"
|
||||
type Body struct {
|
||||
Slideshow struct {
|
||||
|
@ -170,9 +230,6 @@ if err != nil {
|
|||
fmt.Printf("%+v\n", out)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Click here to see the result</summary>
|
||||
|
||||
```text
|
||||
{Slideshow:{Author:Yours Truly Date:date of publication Title:Sample Slide Show}}
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue