requesto

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2025 License: MIT Imports: 17 Imported by: 0

README

requesto

Go Reference

English | [简体中文] | [繁體中文] | [日本語] | [한국어] | [Русский] | [Español] | [Deutsch]

[!TIP] This project is under active development. If you have any great ideas or suggestions, feel free to open an issue or submit a pull request!

requesto is an elegant and powerful HTTP client library for Go. Built on top of the standard net/http library, it offers a fluent, chainable API, a robust middleware system, and a suite of convenient features. It's designed to make sending HTTP requests intuitive and to enhance the developer experience.

🧠 About the Name

    1. There are just too many libraries named requests.
    1. It has a lively and passionate feel, reminiscent of Romance languages, making it catchy and memorable.
    1. You can also see it as a blend of request + go.

⭐ Features

  • 📦️ Out-of-the-box convenience for simple requests, with powerful options for complex scenarios.
  • ✨ An intuitive, fluent, and chainable API.
  • 🚀 Built-in support for JSON, x-www-form-urlencoded, binary data, and file uploads.
  • 🍪 Automatic cookie jar management for sessions.
  • ⏱️ Timeout and cancellation control via context.Context.
  • 🔧 Configurable clients (timeouts, redirect policies, etc.).
  • 🧅 A powerful yet simple middleware (hook) system.

💿 Installation

go get github.com/Kaguya233qwq/requesto

⚡ Quick Start

Send a request and parse a JSON response in just a few lines:

package main

import (
	"fmt"
	"log"

	"github.com/Kaguya233qwq/requesto"
)

func main() {
	resp, err := requesto.Get("https://httpbingo.org/get")
	if err != nil {
		log.Fatalf("Request failed: %v", err)
	}
	fmt.Printf("Status Code: %d\n", resp.StatusCode())
	
	// Parse the response body into a map
	jsonData, _ := resp.Json()
	fmt.Printf("Args from server: %v\n", jsonData["args"])
}

📚 Usage

🚀 Direct Requests
Passing Query Parameters
params := requesto.Params{"q": "1"}
resp, err := requesto.Get("https://example.com", params)
if err != nil {
	log.Fatalf("Request failed: %v", err)
}
fmt.Printf("Status Code: %d\n", resp.StatusCode())
Setting Headers
params := requesto.Params{"q": "1"}
headers := requesto.Headers{"Accept": "text/html,application/json"}
resp, err := requesto.Get("https://example.com", params, headers)
if err != nil {
	log.Fatalf("Request failed: %v", err)
}
fmt.Printf("Status Code: %d\n", resp.StatusCode())
Sending Form Data
form := requesto.AsForm(map[string]string{"arg1": "value1"})
resp, err := requesto.Post("https://httpbingo.org/post", form)
if err != nil {
	log.Fatalf("Request failed: %v", err)
}
fmt.Printf("Status Code: %d\n", resp.StatusCode())
Sending JSON Data
jsonData := requesto.AsJson(map[string]any{"arg1": "value1", "arg2": 0})
resp, err := requesto.Post("https://httpbingo.org/post", jsonData)
if err != nil {
	log.Fatalf("Request failed: %v", err)
}
fmt.Printf("Status Code: %d\n", resp.StatusCode())
🛠️ Using a Client

A Client is reusable and contains a connection pool, cookie jar, and global settings, which acts like a persistent session.

Default Client
client := requesto.NewClient("https://example.com")
Custom Client

NewClient supports functional options to configure timeouts, redirect policies, and more.

client := requesto.NewClient(
    "https://example.com",
    requesto.WithTimeout(10*time.Second),      // Set a global timeout of 10 seconds
    requesto.WithFollowRedirects(false),     // Disable automatic redirects
)

You can set or modify many client properties before making a request:

client := requesto.NewClient("https://example.com")
// Set headers
client.Headers.Set("Content-Type", "text/html; charset=utf-8")
// Set query parameters
client.Params = map[string]string{
    "page": "1",
    "size": "10",
}
// Set cookies from a key-value map
client.SetCookiesFromMap(
    map[string]string{
        "__token": "xyz",
    },
)
client.Get()
Creating a New Request

Use NewRequest() to build a new request for more complex session control:

client := requesto.NewClient("https://api.example.com")
req1 := client.NewRequest()
req2 := client.NewRequest()
// Requests from the same client share a connection pool but are otherwise independent.

Or, you can make a request directly:

client := requesto.NewClient("https://example.com")
resp, err := client.Get()
Request Body

In client mode, requesto supports various ways to set the request body.

Sending JSON

Both structs and map[string]any are supported:

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

user := User{Name: "Alice", Age: 20}
client := requesto.NewClient("https://httpbingo.org")
resp, err := client.NewRequest().
    JoinPath("/post"). // Use JoinPath to dynamically append to the base URL
    SetJsonData(user). // Can be a struct or a map
    Post()
user := map[string]any{"name": "Alice", "age": 20}
client := requesto.NewClient("https://httpbingo.org")
resp, err := client.NewRequest().
    JoinPath("/post").
    SetJsonData(user).
    Post()
Sending Form Data
formData := map[string]string{
    "username": "bob",
    "password": "123",
}

resp, err := client.NewRequest().
    JoinPath("/post").
    SetFormData(formData).
    Post()
Uploading Files
// Assuming 'hello.txt' exists and contains 'hello world'
resp, err := client.NewRequest().
    JoinPath("/post").
    SetFormData(map[string]string{
        "user_id": "123",
    }).
    SetFiles(map[string]string{
        "upload_file": "hello.txt",
    }).
    Post()
Handling Responses

The Response object provides convenient methods for parsing the response body.

resp, _ := client.NewRequest().Get()

// Get status code and headers
statusCode := resp.StatusCode()
headers := resp.Headers()

// Get the response body
text, _ := resp.Text()
bytes, _ := resp.Bytes()

// Parse JSON
var jsonData map[string]any
err := resp.Json(&jsonData) // It's better to pass a pointer to unmarshal into

// Parse cookies from the response
cookies := resp.Cookies()
cookiesMap := resp.CookiesMap()

The Client has a built-in CookieJar that automatically handles session cookies.

// The server sets a cookie, and the client's jar will automatically store it.
client.NewRequest().JoinPath("/cookies/set").SetParams(map[string]string{
    "session_id": "my_secret_session",
}).Get()

// Now, access an authenticated endpoint. The cookie will be sent automatically.
resp, _ := client.NewRequest().JoinPath("/cookies").Get()
// You can inspect the cookies on the client
cookies := client.Cookies()

// You can also manually set cookies on the client
client.SetCookiesFromMap(map[string]string{
    "token": "123",
})
Using Context for Timeouts and Cancellation

When you need to manage concurrent requests, use NewRequestWithContext to pass a context for per-request timeouts or cancellation signals.

// Create a context that will be canceled after 2 seconds
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

// This request will fail due to the context timeout
_, err := client.NewRequestWithContext(ctx).
    JoinPath("/delay/3"). // This endpoint waits for 3 seconds
    Get()

if errors.Is(err, context.DeadlineExceeded) {
    fmt.Println("Request timed out")
}

Advanced: Middleware

Middleware is one of requesto's most powerful features, allowing you to inject custom logic before a request is sent or after a response is received.

Using Middleware

requesto provides some built-in middleware, such as a logger and a retrier.

import "github.com/Kaguya233qwq/requesto/middleware"

client := requesto.NewClient("http://example.com")

// Apply middleware to the client
client.Use(
    middleware.NewLogger(
        middleware.WithLevel(middleware.LevelDebug),
        middleware.WithHeaders(true),
    ),
    middleware.NewRetrier(
        middleware.WithRetryCount(3),
        middleware.WithRetryOnServerErrors(), // Retry on 5xx status codes
    ),
)
Writing Custom Middleware

The recommended way to create simple middleware is with the NewHook builder.

  • Using NewHook

Here's an example of a middleware that adds an authentication header to every request:

func AuthMiddleware(token string) requesto.Middleware {
    return middleware.NewHook(
        middleware.WithBeforeRequest(func(req *requesto.Request) error {
            req.SetHeader("Authorization", "Bearer " + token)
            return nil
        }),
    )
}

client.Use(AuthMiddleware("my_secret_token"))
  • Implementing the Full Interface

For more complex logic that needs to control the request flow (like caching), you can implement the full requesto.Middleware interface:

func MyComplexMiddleware() requesto.Middleware {
    return func(req *requesto.Request, next requesto.Next) (*requesto.Response, error) {
        // Logic before the request...

        // Call the next middleware in the chain
        resp, err := next(req)

        // Logic after the response...

        return resp, err
    }
}

📜 License

This project is licensed under the MIT License. See the LICENSE file for more details.

🙏 Acknowledgements

Special thanks to the following projects for providing inspiration and code references. The open-source community is a better place because of you.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrReadingBody         = errors.New("requesto: error reading response body")
	ErrUnmarshallingJSON   = errors.New("requesto: error unmarshalling JSON response")
	ErrUnmarshallingStruct = errors.New("requesto: error unmarshalling struct from JSON response")
)

Functions

func AsFiles

func AsFiles(data map[string]File) bodyArgument

AsFiles marks the provided map to be used for file uploads.

func AsForm

func AsForm(data map[string]string) bodyArgument

AsForm marks the provided map to be sent as a form-urlencoded request body.

func AsHeaders

func AsHeaders(data map[string]string) bodyArgument

AsHeaders marks the provided map to be used as HTTP request headers.

func AsJson

func AsJson(data any) bodyArgument

AsJson marks the provided data to be sent as a JSON request body.

func AsParams

func AsParams(data map[string]string) bodyArgument

AsParams marks the provided map to be used as URL query parameters.

func ToStruct

func ToStruct[T any](r *Response) (T, error)

ToStruct unmarshals the response body into a new instance of the specified generic type T.

Types

type Client

type Client struct {
	CookieJar http.CookieJar
	BaseURL   string
	Headers   http.Header
	Params    map[string]string
	FormData  map[string]string
	JsonData  any
	BodyBytes []byte
	Files     map[string]File
	// contains filtered or unexported fields
}

Client is a reusable HTTP client that manages a connection pool, cookies, and default settings for requests.

func NewClient

func NewClient(baseUrl string, opts ...ClientOption) *Client

NewClient creates and returns a new Client instance. It initializes a default http.Client with a cookie jar and applies any provided ClientOption functions.

func (*Client) Get

func (c *Client) Get() (*Response, error)

Get creates and sends a new GET request using the client's default settings.

func (*Client) NewRequest

func (c *Client) NewRequest() *Request

NewRequest creates a new Request instance with a default background context.

func (*Client) NewRequestWithContext

func (c *Client) NewRequestWithContext(ctx context.Context) *Request

NewRequestWithContext creates a new Request instance with the provided context. If the provided context is nil, it defaults to context.Background().

func (*Client) Post

func (c *Client) Post() (*Response, error)

Post creates and sends a new POST request using the client's default settings.

func (*Client) SetBinary

func (c *Client) SetBinary(data []byte)

SetBinary sets a default raw binary body for requests made by this client.

func (*Client) SetCookiesFromMap

func (c *Client) SetCookiesFromMap(cookies map[string]string) error

SetCookiesFromMap adds cookies to the client's cookie jar from a map. It requires the client to have a valid BaseURL and returns an error if it's missing or invalid.

func (*Client) SetFiles

func (c *Client) SetFiles(files map[string]File)

SetFiles sets default files to be uploaded with requests made by this client.

func (*Client) SetFormData

func (c *Client) SetFormData(data map[string]string)

SetFormData sets a default form-urlencoded body for requests made by this client.

func (*Client) SetHeaders

func (c *Client) SetHeaders(h map[string]string)

SetHeaders sets default headers that will be sent with every request from this client.

func (*Client) SetJsonData

func (c *Client) SetJsonData(data any)

SetJsonData sets a default JSON body for requests made by this client.

func (*Client) SetParams

func (c *Client) SetParams(params map[string]string)

SetParams sets default query parameters that will be added to every request from this client.

func (*Client) Use

func (c *Client) Use(middlewares ...Middleware)

Use adds one or more middleware handlers to the client's middleware chain.

type ClientOption

type ClientOption func(*clientConfig)

ClientOption is a function that configures a Client.

func WithFollowRedirects

func WithFollowRedirects(follow bool) ClientOption

WithFollowRedirects controls whether the client should automatically follow HTTP redirects. The default behavior is to follow redirects (true).

func WithTimeout

func WithTimeout(timeout time.Duration) ClientOption

WithTimeout sets the global request timeout for the http.Client.

func WithTransport

func WithTransport(transport http.RoundTripper) ClientOption

WithTransport allows setting a custom http.RoundTripper (transport) for the client.

type ConcurrencyManager

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

ConcurrencyManager manages and executes a pool of concurrent HTTP requests.

func NewManager

func NewManager(client *Client, opts ...ManagerOption) *ConcurrencyManager

NewManager creates and returns a new ConcurrencyManager. It takes a configured requesto.Client and a set of options to initialize its settings.

func (*ConcurrencyManager) AddTasks

func (cm *ConcurrencyManager) AddTasks(tasks ...Task) *ConcurrencyManager

AddTasks adds one or more pre-configured Tasks to the manager's queue.

func (*ConcurrencyManager) AddURLs

func (cm *ConcurrencyManager) AddURLs(urls ...string) *ConcurrencyManager

AddURLs is a helper function to quickly create and add tasks from a list of URL strings.

func (*ConcurrencyManager) Run

func (cm *ConcurrencyManager) Run() []Result

Run starts the worker pool, executes all queued tasks, and blocks until they are all completed. It returns a slice of Results. The order of the results is not guaranteed to match the order in which tasks were added.

type File

type File struct {
	Name    string
	Content io.Reader
}

File represents a file to be uploaded, containing its name and content as an io.Reader.

func FileFromBytes

func FileFromBytes(fileName string, data []byte) File

FileFromBytes creates a File object from a byte slice. It uses the provided file name and wraps the byte slice in an io.Reader.

func FileFromPath

func FileFromPath(filePath string) (File, error)

FileFromPath creates a File object from a given file path. It opens the file and uses its base name as the file name.

type Files

type Files map[string]File

Files is a type alias for a map of files to be uploaded.

type FormData

type FormData map[string]string

FormData is a type alias for a map of form data.

type Headers

type Headers map[string]string

Headers is a type alias for a map of request headers.

type JsonData

type JsonData any

JsonData is a type alias for any data that can be marshaled to JSON.

type ManagerOption

type ManagerOption func(*managerConfig)

ManagerOption is a function type for configuring a ConcurrencyManager.

func WithContext

func WithContext(ctx context.Context) ManagerOption

WithContext sets a master context for the entire set of concurrent tasks, useful for global timeouts or cancellations.

func WithPoolSize

func WithPoolSize(size int) ManagerOption

WithPoolSize sets the size of the concurrency pool, which determines the number of worker goroutines running in parallel.

type Middleware

type Middleware func(req *Request, next Next) (*Response, error)

Middleware defines the function signature for a requesto middleware.

type Next

type Next func(req *Request) (*Response, error)

Next defines the next handler in the middleware chain. A middleware must call the next function to pass the request along to the next handler.

type Params

type Params map[string]string

Params is a type alias for a map of URL query parameters.

type Request

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

Request represents a single HTTP request that can be configured and sent.

func (*Request) Cookies

func (r *Request) Cookies() []*http.Cookie

Cookies parses and returns any cookies set in the request headers.

func (*Request) CookiesMap

func (r *Request) CookiesMap() map[string]string

CookiesMap parses any cookies set in the request headers and returns them as a map.

func (*Request) Delete

func (r *Request) Delete() (*Response, error)

Delete sets the method to DELETE and sends the request.

func (*Request) Get

func (r *Request) Get() (*Response, error)

Get sets the method to GET and sends the request.

func (*Request) JoinPath

func (r *Request) JoinPath(p string) *Request

JoinPath intelligently appends a path segment to the request's URL. If the provided path `p` is an absolute URL, it will replace the current request URL entirely. Otherwise, it joins the path to the existing URL's path.

func (*Request) Post

func (r *Request) Post() (*Response, error)

Post sets the method to POST and sends the request.

func (*Request) Put

func (r *Request) Put() (*Response, error)

Put sets the method to PUT and sends the request.

func (*Request) SetBinary

func (r *Request) SetBinary(data []byte) *Request

SetBinary sets the request body to the provided raw byte slice. If the Content-Type header is not already set, it defaults to "application/octet-stream".

func (*Request) SetCookiesFromMap

func (r *Request) SetCookiesFromMap(cookies map[string]string) *Request

SetCookiesFromMap adds cookies to the client's underlying cookie jar from a map. This requires the client to have a valid BaseURL to determine the cookie domain.

func (*Request) SetFiles

func (r *Request) SetFiles(files map[string]File) *Request

SetFiles sets the files to be uploaded as part of a multipart/form-data request.

func (*Request) SetFormData

func (r *Request) SetFormData(data map[string]string) *Request

SetFormData sets the request body to be form-urlencoded from the provided map. It also sets the Content-Type header to "application/x-www-form-urlencoded".

func (*Request) SetHeaders

func (r *Request) SetHeaders(h map[string]string) *Request

SetHeaders sets the request headers.

func (*Request) SetJsonData

func (r *Request) SetJsonData(data any) *Request

SetJsonData sets the request body to be JSON-encoded from the provided data (struct or map). It also sets the Content-Type header to "application/json; charset=utf-8".

func (*Request) SetParams

func (r *Request) SetParams(params map[string]string) *Request

SetParams sets the URL query parameters for the request.

func (*Request) SetURL

func (r *Request) SetURL(rawURL string) *Request

SetURL sets the raw URL for the request, replacing any existing URL.

type Response

type Response struct {
	Resp *http.Response
	// contains filtered or unexported fields
}

Response wraps the standard http.Response to provide convenient access to the response body and other common properties. It also stores any error that occurred during the reading of the response body.

func Get

func Get(URL string, args ...any) (*Response, error)

Get sends a GET request to the specified URL. It accepts optional arguments, which can be of type Params or Headers. Body-related arguments are ignored. An error is returned for any unsupported argument type.

func Post

func Post(URL string, args ...any) (*Response, error)

Post sends a convenient POST request.

It accepts a URL and one or more optional arguments, which can be: - Headers: to set request headers. - Params: to set URL query parameters. - AsJson, AsForm, or AsFiles: to set the request body.

func (*Response) Bytes

func (r *Response) Bytes() ([]byte, error)

Bytes returns the response body as a byte slice.

func (*Response) Cookies

func (r *Response) Cookies() []*http.Cookie

Cookies returns a slice of *http.Cookie containing all cookies set by the server in the response.

func (*Response) CookiesMap

func (r *Response) CookiesMap() map[string]string

CookiesMap parses all response cookies into a map of name-value pairs.

func (*Response) Header

func (r *Response) Header() http.Header

Header returns the response headers.

func (*Response) Json

func (r *Response) Json() (json_results map[string]any, err error)

Json unmarshals the response body into a map[string]any.

func (*Response) StatusCode

func (r *Response) StatusCode() int

StatusCode returns the HTTP status code of the response. It returns -1 if an error occurred while reading the response body.

func (*Response) Text

func (r *Response) Text() (string, error)

Text returns the response body as a string.

func (*Response) Unmarshal

func (r *Response) Unmarshal(v any) error

Unmarshal unmarshals the response body into the provided value `v`, which should be a pointer.

type Result

type Result struct {
	TaskID   string
	Response *Response
	Error    error
}

Result encapsulates the outcome of a concurrent request, containing either a Response or an Error, along with the ID of the original Task.

type Task

type Task struct {
	ID      string
	Request *Request
}

Task represents a single, independent request to be executed concurrently.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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