inrequest

package module
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2025 License: MIT Imports: 8 Imported by: 0

README

Go Inrequest

Go Version License Go Report Card

Golang package for transforming HTTP request body into Go maps and structs.

Supports multiple content types:

  • multipart/form-data (with file uploads)
  • application/x-www-form-urlencoded
  • application/json
  • Query string parameters

Installation

Requires Go 1.17+

go get github.com/ezartsh/inrequest

Import

import "github.com/ezartsh/inrequest"

Quick Start

package main

import (
    "fmt"
    "net/http"
    "github.com/ezartsh/inrequest"
)

func main() {
    http.HandleFunc("/submit", func(w http.ResponseWriter, r *http.Request) {
        // Parse form data
        req := inrequest.FormData(r)
        defer req.Cleanup() // Optional: clean up temp files
        
        fmt.Println(req.ToMap())
        // output: map[first_name:John last_name:Smith]
    })
    
    http.ListenAndServe(":8080", nil)
}

API Reference

Entry Functions
Function Description
FormData(r *http.Request) Parse multipart/form-data or url-encoded form
FormDataWithOptions(r *http.Request, opts Options) Parse form with custom options
Query(r *http.Request) Parse query string parameters
Json(r *http.Request) Parse JSON request body
Parse(r *http.Request) Auto-detect and parse based on Content-Type
Options
type Options struct {
    MaxMemory int64 // Max memory for multipart parsing (default: 32MB)
}
Request Interface

All request types implement the Request interface:

type Request interface {
    ToMap() RequestValue                    // Get as map[string]interface{}
    ToBind(model interface{}) error         // Bind to struct
    ToJsonByte() ([]byte, error)            // Get as JSON bytes
    ToJsonString() (string, error)          // Get as JSON string
}
FormRequest Additional Methods
// Cleanup removes temporary files created during multipart form parsing.
// Optional but recommended for high-traffic servers.
func (r FormRequest) Cleanup()
Types
// RequestValue is the parsed request data
type RequestValue = map[string]interface{}

// FileHeaders represents multiple uploaded files for a single field
type FileHeaders []*multipart.FileHeader

Request Body Types


1. Form Data

Basic Example
<form action="/submit" method="POST">
    <input type="text" value="John" name="first_name"/>
    <input type="text" value="Smith" name="last_name"/>
</form>
package main

import (
    "fmt"
    "net/http"
    "github.com/ezartsh/inrequest"
)

type BodyRequest struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
}

func main() {
    http.HandleFunc("/submit", func(w http.ResponseWriter, r *http.Request) {
        req := inrequest.FormData(r)
        defer req.Cleanup()
        
        // Get as map
        fmt.Println(req.ToMap())
        // output: map[first_name:John last_name:Smith]
        
        // Bind to struct
        var body BodyRequest
        req.ToBind(&body)
        fmt.Printf("My name is %s %s\n", body.FirstName, body.LastName)
        // output: My name is John Smith
        
        // Get as JSON string
        if jsonStr, err := req.ToJsonString(); err == nil {
            fmt.Println(jsonStr)
            // output: {"first_name":"John","last_name":"Smith"}
        }
    })
    
    http.ListenAndServe(":8080", nil)
}
Nested Arrays and Objects
<form action="/submit" method="POST" enctype="multipart/form-data">
    <input type="text" value="John" name="names[0]"/>
    <input type="text" value="Smith" name="names[1]"/>
    <input type="text" value="Doe" name="names[2]"/>
    
    <input type="text" value="Title 1" name="attachments[0][title]"/>
    <textarea name="attachments[0][description]">Description 1</textarea>
    <input type="file" name="attachments[0][file]"/>
    
    <input type="text" value="Title 2" name="attachments[1][title]"/>
    <textarea name="attachments[1][description]">Description 2</textarea>
    <input type="file" name="attachments[1][file]"/>
</form>
req := inrequest.FormData(r)
defer req.Cleanup()

jsonStr, _ := req.ToJsonString()
fmt.Println(jsonStr)

Output:

{
    "names": ["John", "Smith", "Doe"],
    "attachments": [
        {
            "title": "Title 1",
            "description": "Description 1",
            "file": {
                "Filename": "document.pdf",
                "Header": {
                    "Content-Type": ["application/pdf"]
                },
                "Size": 13264
            }
        },
        {
            "title": "Title 2",
            "description": "Description 2",
            "file": {
                "Filename": "image.png",
                "Header": {
                    "Content-Type": ["image/png"]
                },
                "Size": 8192
            }
        }
    ]
}
Multiple File Uploads

For multiple files with the same field name, use FileHeaders:

<input type="file" name="documents[]" multiple/>
type UploadRequest struct {
    Documents inrequest.FileHeaders `json:"documents"`
}

req := inrequest.FormData(r)
defer req.Cleanup()

var upload UploadRequest
req.ToBind(&upload)

for _, file := range upload.Documents {
    fmt.Printf("File: %s, Size: %d\n", file.Filename, file.Size)
}
Custom Memory Limit
req := inrequest.FormDataWithOptions(r, inrequest.Options{
    MaxMemory: 10 << 20, // 10 MB
})
defer req.Cleanup()

2. Query String

GET /search?utm_source=google&callback_url=http://localhost:3000&status=active
package main

import (
    "fmt"
    "net/http"
    "github.com/ezartsh/inrequest"
)

func main() {
    http.HandleFunc("/search", func(w http.ResponseWriter, r *http.Request) {
        req := inrequest.Query(r)
        
        fmt.Println(req.ToMap())
        // output: map[callback_url:http://localhost:3000 status:active utm_source:google]
        
        if jsonStr, err := req.ToJsonString(); err == nil {
            fmt.Println(jsonStr)
            // output: {"callback_url":"http://localhost:3000","status":"active","utm_source":"google"}
        }
    })
    
    http.ListenAndServe(":8080", nil)
}
Nested Query Parameters
GET /filter?filters[status]=active&filters[category]=tech&items[0]=a&items[1]=b
req := inrequest.Query(r)
fmt.Println(req.ToMap())
// output: map[filters:map[status:active category:tech] items:[a b]]

3. JSON Request

{
    "first_name": "John",
    "last_name": "Smith",
    "age": 31
}
package main

import (
    "fmt"
    "net/http"
    "github.com/ezartsh/inrequest"
)

type Person struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    Age       int    `json:"age"`
}

func main() {
    http.HandleFunc("/api/person", func(w http.ResponseWriter, r *http.Request) {
        req := inrequest.Json(r)
        
        fmt.Println(req.ToMap())
        // output: map[age:31 first_name:John last_name:Smith]
        
        var person Person
        req.ToBind(&person)
        fmt.Printf("%s is %d years old\n", person.FirstName, person.Age)
        // output: John is 31 years old
        
        if jsonStr, err := req.ToJsonString(); err == nil {
            fmt.Println(jsonStr)
            // output: {"age":31,"first_name":"John","last_name":"Smith"}
        }
    })
    
    http.ListenAndServe(":8080", nil)
}

4. Auto Parse

Use Parse() to automatically detect and parse based on Content-Type:

http.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
    req := inrequest.Parse(r)
    
    // Works with any content type:
    // - application/json
    // - multipart/form-data
    // - application/x-www-form-urlencoded
    // - Falls back to query string
    
    fmt.Println(req.ToMap())
})

Type Conversion

The library automatically converts string values to appropriate Go types:

Input Output Type
"123" int
"3.14" float64
"true", "false" bool
"null" nil
Other strings string

Binding to Structs

Use ToBind() to bind request data to a struct. The struct fields should have json tags:

type CreateUserRequest struct {
    Name     string                  `json:"name"`
    Email    string                  `json:"email"`
    Age      int                     `json:"age"`
    Active   bool                    `json:"active"`
    Avatar   *multipart.FileHeader   `json:"avatar"`      // Single file
    Photos   inrequest.FileHeaders   `json:"photos"`      // Multiple files
}

req := inrequest.FormData(r)
defer req.Cleanup()

var user CreateUserRequest
if err := req.ToBind(&user); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}

// Access file
if user.Avatar != nil {
    fmt.Printf("Avatar: %s\n", user.Avatar.Filename)
}

// Access multiple files
for _, photo := range user.Photos {
    fmt.Printf("Photo: %s\n", photo.Filename)
}

Performance

The library is optimized for performance:

Operation Time
Simple form (5 fields) ~5 µs
Complex nested form ~50 µs
Large form (100 fields) ~200 µs
File upload ~500 µs

Memory allocation is minimal, and temporary files are cleaned up with Cleanup().


Contributing

If you have a bug report or feature request, you can open an issue, and pull requests are also welcome.

License

inrequest is released under the MIT license. See LICENSE for details.

Documentation

Overview

Package inrequest provides utilities for transforming HTTP request bodies into Go maps and structs.

It supports multiple content types including multipart/form-data, application/x-www-form-urlencoded, application/json, and query string parameters.

Basic Usage

Parse form data from an HTTP request:

req := inrequest.FormData(r)
defer req.Cleanup() // Optional: clean up temp files

// Get as map
data := req.ToMap()

// Bind to struct
var user User
req.ToBind(&user)

// Get as JSON
jsonStr, _ := req.ToJsonString()

Request Types

The package provides four main entry functions:

  • FormData: Parse multipart/form-data or application/x-www-form-urlencoded
  • FormDataWithOptions: Parse form data with custom options (e.g., max memory)
  • Query: Parse query string parameters
  • Json: Parse JSON request body
  • Parse: Auto-detect content type and parse accordingly

Nested Data

The package supports bracket notation for nested structures:

// Input: items[0][name]=foo&items[0][price]=10&items[1][name]=bar
// Output: map[items:[]map[name:foo price:10] map[name:bar]]

File Uploads

For file uploads, use FormData with multipart/form-data:

type Upload struct {
    Title    string                  `json:"title"`
    Document *multipart.FileHeader   `json:"document"`  // Single file
    Photos   inrequest.FileHeaders   `json:"photos"`    // Multiple files
}

req := inrequest.FormData(r)
defer req.Cleanup()

var upload Upload
req.ToBind(&upload)

Type Conversion

String values are automatically converted to appropriate Go types:

  • "123" → int
  • "3.14" → float64
  • "true", "false" → bool
  • "null" → nil
  • Leading zeros preserved: "007" → "007" (string)

Error Handling

The package provides custom error types for better error handling:

req := inrequest.FormData(r)
var user User
if err := req.ToBind(&user); err != nil {
    if inrequest.IsBindError(err) {
        // Handle binding error
    }
}

Performance

The library is optimized for performance with minimal allocations:

  • Simple form (5 fields): ~5 µs
  • Complex nested form: ~50 µs
  • Large form (100 fields): ~200 µs

For high-traffic servers, call FormRequest.Cleanup to immediately release temporary files created during multipart parsing.

Index

Constants

View Source
const DefaultMaxMemory = 32 << 20

Default max memory for multipart form parsing (32MB)

Variables

This section is empty.

Functions

func IsBindError added in v1.3.0

func IsBindError(err error) bool

IsBindError returns true if the error is a BindError

func IsParseError added in v1.3.0

func IsParseError(err error) bool

IsParseError returns true if the error is a ParseError

Types

type BindError added in v1.3.0

type BindError struct {
	Field   string // Field name that caused the error (if known)
	Message string // Error message
	Err     error  // Underlying error
}

BindError represents an error that occurred during struct binding

func NewBindError added in v1.3.0

func NewBindError(field, message string, err error) *BindError

NewBindError creates a new BindError

func (*BindError) Error added in v1.3.0

func (e *BindError) Error() string

func (*BindError) Unwrap added in v1.3.0

func (e *BindError) Unwrap() error

type FileHeaders added in v1.3.0

type FileHeaders []*multipart.FileHeader

FileHeaders represents multiple uploaded files for a single field

type FormRequest added in v1.2.2

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

func FormData added in v1.1.0

func FormData(r *http.Request) FormRequest

FormData parses multipart/form-data and application/x-www-form-urlencoded requests. It automatically handles nested fields, arrays, and file uploads.

Basic usage:

form := inrequest.FormData(r)
data := form.ToMap()

With struct binding:

var user User
inrequest.FormData(r).ToBind(&user)

To clean up temporary files immediately (optional, for high-traffic servers):

form := inrequest.FormData(r)
defer form.Cleanup()

func FormDataWithOptions added in v1.3.0

func FormDataWithOptions(r *http.Request, maxMemory int64) FormRequest

FormDataWithOptions parses form data with a custom max memory limit. maxMemory is the maximum bytes stored in memory (excess goes to temp files).

func (FormRequest) Cleanup added in v1.3.0

func (r FormRequest) Cleanup()

Cleanup removes temporary files created during multipart form parsing. This is optional - Go's garbage collector will eventually clean up, but calling this explicitly is recommended for high-traffic servers.

func (FormRequest) ToBind added in v1.2.2

func (r FormRequest) ToBind(model interface{}) error

ToBind binds the form data to a struct. It handles both regular fields via JSON marshaling and special types like *multipart.FileHeader.

func (FormRequest) ToJsonByte added in v1.2.2

func (r FormRequest) ToJsonByte() ([]byte, error)

func (FormRequest) ToJsonString added in v1.2.2

func (r FormRequest) ToJsonString() (string, error)

func (FormRequest) ToMap added in v1.2.2

func (r FormRequest) ToMap() RequestValue

type GroupRequest

type GroupRequest map[string][]GroupRequestProperty

type GroupRequestProperty

type GroupRequestProperty struct {
	Path  string
	Value interface{}
}

type JsonRequest added in v1.2.2

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

func Json added in v1.1.0

func Json(r *http.Request) (JsonRequest, error)

Json parses a JSON request body.

Usage:

jsonReq, err := inrequest.Json(r)
if err != nil {
    // handle error
}
data := jsonReq.ToMap()

func (JsonRequest) ToBind added in v1.2.2

func (r JsonRequest) ToBind(model interface{}) error

func (JsonRequest) ToByte added in v1.2.2

func (r JsonRequest) ToByte() ([]byte, error)

ToByte is deprecated, use ToJsonByte instead. Deprecated: Use ToJsonByte for consistent API.

func (JsonRequest) ToJsonByte added in v1.3.0

func (r JsonRequest) ToJsonByte() ([]byte, error)

func (JsonRequest) ToJsonString added in v1.3.0

func (r JsonRequest) ToJsonString() (string, error)

func (JsonRequest) ToMap added in v1.2.2

func (r JsonRequest) ToMap() RequestValue

func (JsonRequest) ToString added in v1.2.2

func (r JsonRequest) ToString() (string, error)

ToString is deprecated, use ToJsonString instead. Deprecated: Use ToJsonString for consistent API.

type ParseError added in v1.3.0

type ParseError struct {
	Type    string // Type of request being parsed (form, json, query)
	Message string // Error message
	Err     error  // Underlying error
}

ParseError represents an error that occurred during request parsing

func NewParseError added in v1.3.0

func NewParseError(reqType, message string, err error) *ParseError

NewParseError creates a new ParseError

func (*ParseError) Error added in v1.3.0

func (e *ParseError) Error() string

func (*ParseError) Unwrap added in v1.3.0

func (e *ParseError) Unwrap() error

type QueryRequest added in v1.2.2

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

func Query added in v1.1.0

func Query(r *http.Request) QueryRequest

Query parses URL query parameters.

Usage:

query := inrequest.Query(r)
data := query.ToMap()

func (QueryRequest) ToBind added in v1.2.2

func (r QueryRequest) ToBind(model interface{}) error

func (QueryRequest) ToJsonByte added in v1.2.2

func (r QueryRequest) ToJsonByte() ([]byte, error)

func (QueryRequest) ToJsonString added in v1.2.2

func (r QueryRequest) ToJsonString() (string, error)

func (QueryRequest) ToMap added in v1.2.2

func (r QueryRequest) ToMap() RequestValue

type Request added in v1.3.0

type Request interface {
	ToMap() RequestValue
	ToBind(model interface{}) error
	ToJsonByte() ([]byte, error)
	ToJsonString() (string, error)
}

Request is the common interface for all request types

func Parse added in v1.3.0

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

Parse automatically detects the content type and parses the request.

Usage:

req, err := inrequest.Parse(r)
if err != nil {
    // handle error (only for JSON)
}
data := req.ToMap()

type RequestValue

type RequestValue = map[string]interface{}

RequestValue is the map type used to store parsed request data

Jump to

Keyboard shortcuts

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