Documentation
¶
Overview ¶
Package decoder provides components for decoding HTTP request bodies of various content types.
Decoders implement the Decoder interface and are responsible for parsing request bodies based on the Content-Type header. Each decoder handles a specific content type and populates Go structures accordingly.
Built-in Decoders ¶
- JSON: Handles application/json using jsoniter for performance
- XML: Handles application/xml using standard library encoding/xml
- FormURL: Handles application/x-www-form-urlencoded for HTML forms
- MultipartFormData: Handles multipart/form-data for file uploads and complex forms
Basic Usage ¶
// Create decoders
jsonDecoder := decoder.NewJSON()
formDecoder := decoder.NewFormURL()
// Use with roamer
r := roamer.NewRoamer(
roamer.WithDecoders(jsonDecoder, formDecoder),
)
Custom Decoders ¶
Implement the Decoder interface to support custom content types:
type MyDecoder struct{}
func (d *MyDecoder) Decode(r *http.Request, ptr any) error {
// Custom decoding logic
return nil
}
func (d *MyDecoder) ContentType() string {
return "application/my-format"
}
func (d *MyDecoder) Tag() string {
return "myformat"
}
Thread Safety ¶
All built-in decoders are safe for concurrent use and should be reused across multiple requests for optimal performance.
Index ¶
- Constants
- func WithContentType[T contentTypeSetter](contentType string) func(T)
- func WithSkipFilled[T skipFilledSetter](skip bool) func(T)
- type FormURL
- type FormURLOptionsFunc
- type JSON
- type JSONOptionsFunc
- type MultipartFile
- type MultipartFiles
- type MultipartFormData
- type MultipartFormDataOptionsFunc
- type XML
- type XMLOptionsFunc
Examples ¶
Constants ¶
const ( // ContentTypeFormURL is the Content-Type header value for URL-encoded form data. // This is used to match requests with the appropriate decoder. ContentTypeFormURL = "application/x-www-form-urlencoded" // TagForm is the struct tag name used for URL-encoded form values. TagForm = "form" // SplitSymbol is the default character used to split form values when // multiple values are provided for the same field. SplitSymbol = "," )
const ( // ContentTypeJSON is the Content-Type header value for JSON requests. // This is used to match requests with the appropriate decoder. ContentTypeJSON = "application/json" // TagJSON is the struct tag name used for JSON field mapping. TagJSON = "json" )
const ( // ContentTypeMultipartFormData is the Content-Type header value for multipart form data. // This is used to match requests with the appropriate decoder. ContentTypeMultipartFormData = "multipart/form-data" // TagMultipart is the struct tag name used for multipart form values. TagMultipart = "multipart" )
const ( // ContentTypeXML is the Content-Type header value for XML requests. // This is used to match requests with the appropriate decoder. ContentTypeXML = "application/xml" // TagXML is the struct tag name used for XML field mapping. TagXML = "xml" )
Variables ¶
This section is empty.
Functions ¶
func WithContentType ¶
func WithContentType[T contentTypeSetter](contentType string) func(T)
WithContentType sets the Content-Type header value that a decoder will handle. Works with any decoder implementing contentTypeSetter interface.
Example:
// Handle custom content types
jsonDecoder := decoder.NewJSON(
decoder.WithContentType("application/x-json"),
)
xmlDecoder := decoder.NewXML(
decoder.WithContentType("text/xml"),
)
func WithSkipFilled ¶ added in v1.7.1
func WithSkipFilled[T skipFilledSetter](skip bool) func(T)
WithSkipFilled controls whether decoders skip fields with non-zero values. When true, existing values won't be overwritten by decoded values.
Example:
// Overwrite all fields, even if already filled
formDecoder := decoder.NewFormURL(
decoder.WithSkipFilled(false),
)
Types ¶
type FormURL ¶
type FormURL struct {
// contains filtered or unexported fields
}
FormURL is a decoder for handling URL-encoded form data (application/x-www-form-urlencoded).
This decoder processes HTML form submissions and parses them into Go structures. It supports both single and multiple values for the same form field, with automatic splitting of comma-separated values.
Value Splitting ¶
By default, comma-separated form values are split into slices:
- "tags=foo,bar,baz" -> []string{"foo", "bar", "baz"}
This behavior can be disabled using WithDisabledSplit().
Supported Target Types ¶
- Structs with "form" tags
- map[string]string
- map[string]any
- map[string][]string
Thread Safety ¶
The FormURL decoder is safe for concurrent use across multiple goroutines.
func NewFormURL ¶
func NewFormURL(opts ...FormURLOptionsFunc) *FormURL
NewFormURL creates a FormURL decoder that handles application/x-www-form-urlencoded content. By default, it skips already filled fields and splits comma-separated values.
Example:
// Default form decoder
formDecoder := decoder.NewFormURL()
// Custom configuration
formDecoder := decoder.NewFormURL(
decoder.WithDisabledSplit(), // Don't split comma-separated values
decoder.WithSplitSymbol(";"), // Use semicolon as separator
)
// Example struct
type SearchRequest struct {
Query string `form:"q"`
Tags []string `form:"tags"` // Can handle "tags=foo,bar,baz"
}
func (*FormURL) ContentType ¶
ContentType returns the Content-Type header value that this decoder handles. For the FormURL decoder, this is "application/x-www-form-urlencoded" by default. This method is used by the roamer package to match requests with the appropriate decoder.
func (*FormURL) Decode ¶
Decode parses URL-encoded form data into a struct or map. For structs, uses the "form" tag to map fields. For maps, populates with form field names as keys.
Parameters:
- r: The HTTP request with form data.
- ptr: Target pointer (struct or map).
Returns:
- error: Error if decoding fails, nil if successful.
type FormURLOptionsFunc ¶
type FormURLOptionsFunc func(*FormURL)
FormURLOptionsFunc is a function type for configuring a FormURL decoder. It follows the functional options pattern to provide a clean and extensible API.
func WithDisabledSplit ¶
func WithDisabledSplit() FormURLOptionsFunc
WithDisabledSplit disables automatic splitting of form values into slices.
Example: With splitting disabled, "tags=foo,bar,baz" will parse as a single string "foo,bar,baz" rather than a slice ["foo", "bar", "baz"]
func WithSplitSymbol ¶
func WithSplitSymbol(splitSymbol string) FormURLOptionsFunc
WithSplitSymbol sets the character used for splitting form values (default: comma).
Example: With splitSymbol set to ";", a form field "tags=foo;bar;baz" will parse as a slice ["foo", "bar", "baz"]
type JSON ¶
type JSON struct {
// contains filtered or unexported fields
}
JSON is a decoder for handling JSON request bodies.
It uses the jsoniter library (github.com/json-iterator/go) for better performance compared to the standard library's encoding/json. The decoder is configured to be compatible with the standard library's behavior by default.
Performance ¶
jsoniter provides significant performance improvements over encoding/json:
- Faster encoding and decoding
- Lower memory allocations
- API-compatible with standard library
Thread Safety ¶
The JSON decoder is safe for concurrent use across multiple goroutines.
func NewJSON ¶
func NewJSON(opts ...JSONOptionsFunc) *JSON
NewJSON creates a JSON decoder for handling application/json content type.
By default, the decoder uses jsoniter with standard library compatible configuration, which provides better performance while maintaining the same behavior as encoding/json.
Configuration ¶
The decoder can be customized using functional options:
- WithContentType: Override the default "application/json" content type
Default Behavior ¶
- Content-Type: application/json
- Tag name: json
- Configuration: Compatible with standard library
- Empty body handling: Treated as empty object/value
Parameters:
- opts: Optional configuration functions to customize the decoder.
Returns:
- *JSON: A configured JSON decoder instance.
Example:
// Basic JSON decoder
jsonDecoder := decoder.NewJSON()
// JSON decoder with custom content type
jsonDecoder := decoder.NewJSON(
decoder.WithContentType("application/x-json"),
)
// Use with roamer
r := roamer.NewRoamer(roamer.WithDecoders(jsonDecoder))
// Example struct
type UserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age,omitempty"`
}
Example ¶
ExampleNewJSON demonstrates how to create and use a JSON decoder.
package main
import (
"bytes"
"fmt"
"net/http"
"github.com/slipros/roamer"
"github.com/slipros/roamer/decoder"
)
// readCloser wraps a bytes.Buffer to implement io.ReadCloser
type readCloser struct {
*bytes.Buffer
}
func (rc *readCloser) Close() error {
return nil
}
func main() {
// Define a structure for JSON data
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// Create a JSON decoder
jsonDecoder := decoder.NewJSON()
// Create a roamer instance with the JSON decoder
r := roamer.NewRoamer(
roamer.WithDecoders(jsonDecoder),
)
// Create a request with JSON body
jsonBody := `{"id": 123, "name": "John Doe", "email": "[email protected]"}`
req := &http.Request{
Method: "POST",
Header: http.Header{
"Content-Type": {"application/json"},
},
Body: &readCloser{bytes.NewBufferString(jsonBody)},
ContentLength: int64(len(jsonBody)),
}
// Parse the request
var user User
err := r.Parse(req, &user)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("User ID: %d\n", user.ID)
fmt.Printf("User Name: %s\n", user.Name)
fmt.Printf("User Email: %s\n", user.Email)
}
Output: User ID: 123 User Name: John Doe User Email: [email protected]
func (*JSON) ContentType ¶
ContentType returns the Content-Type header value that this decoder handles. For the JSON decoder, this is "application/json" by default. This method is used by the roamer package to match requests with the appropriate decoder.
Example ¶
ExampleJSON_ContentType demonstrates retrieving the content type handled by a JSON decoder.
package main
import (
"fmt"
"github.com/slipros/roamer/decoder"
)
func main() {
d := decoder.NewJSON()
fmt.Printf("Content-Type: %s\n", d.ContentType())
fmt.Printf("Struct Tag: %s\n", d.Tag())
}
Output: Content-Type: application/json Struct Tag: json
func (*JSON) Decode ¶
Decode parses a JSON request body into the provided pointer.
The method reads the request body from r.Body and decodes it into the destination pointed to by ptr using jsoniter for high-performance parsing. The decoder is compatible with standard library's encoding/json behavior.
Empty Body Handling ¶
If the request body is empty (io.EOF), the method returns nil without error, leaving the destination in its zero state. This allows graceful handling of requests with no body content.
Error Handling ¶
The method returns an error if:
- JSON syntax is invalid
- JSON structure doesn't match the destination type
- Required fields are missing (when using json:",required" tag)
- Type conversion fails
Errors from jsoniter are returned directly and can be examined for details.
Supported Types ¶
- Structs with json tags
- Maps (map[string]any, map[string]string, etc.)
- Slices and arrays
- Basic types (when ptr is *string, *int, etc.)
Parameters:
- r: The HTTP request containing the JSON body. Body will be read but not closed.
- ptr: A pointer to the destination where decoded data will be stored. Must not be nil.
Returns:
- error: An error if decoding fails, or nil if successful.
Example:
type UserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
var user UserRequest
err := jsonDecoder.Decode(r, &user)
if err != nil {
// Handle JSON parsing error
return fmt.Errorf("invalid JSON: %w", err)
}
Example ¶
ExampleJSON_Decode demonstrates direct use of the JSON decoder's Decode method.
package main
import (
"bytes"
"fmt"
"net/http"
"github.com/slipros/roamer/decoder"
)
// readCloser wraps a bytes.Buffer to implement io.ReadCloser
type readCloser struct {
*bytes.Buffer
}
func (rc *readCloser) Close() error {
return nil
}
func main() {
// Define a structure
type Product struct {
Name string `json:"name"`
Price float64 `json:"price"`
}
// Create decoder
jsonDecoder := decoder.NewJSON()
// Create request with JSON body
jsonBody := `{"name": "Laptop", "price": 999.99}`
req := &http.Request{
Method: "POST",
Header: http.Header{
"Content-Type": {"application/json"},
},
Body: &readCloser{bytes.NewBufferString(jsonBody)},
ContentLength: int64(len(jsonBody)),
}
// Decode directly
var product Product
err := jsonDecoder.Decode(req, &product)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Product: %s, Price: $%.2f\n", product.Name, product.Price)
}
Output: Product: Laptop, Price: $999.99
type JSONOptionsFunc ¶
type JSONOptionsFunc = func(*JSON)
JSONOptionsFunc is a function type for configuring a JSON decoder. It follows the functional options pattern to provide a clean and extensible API.
type MultipartFile ¶
type MultipartFile struct {
// Key is the form field name associated with this file.
Key string
// File is the actual file content, which can be read as an io.Reader.
// Remember to close this file when you're done with it to avoid resource leaks.
File multipart.File
// Header contains metadata about the uploaded file,
// such as the original filename, content type, and size.
Header *multipart.FileHeader
}
MultipartFile represents an uploaded file from a multipart/form-data request. Contains both file content and metadata.
Example:
type UploadRequest struct {
Avatar *MultipartFile `multipart:"avatar"`
}
func (*MultipartFile) ContentType ¶
func (f *MultipartFile) ContentType() string
ContentType returns the content type of the uploaded file.
Example:
// Check file type
contentType := req.Avatar.ContentType()
if contentType != "image/jpeg" && contentType != "image/png" {
http.Error(w, "Only JPEG and PNG allowed", http.StatusBadRequest)
return
}
func (*MultipartFile) Copy ¶
func (f *MultipartFile) Copy() (MultipartFile, error)
Copy creates a duplicate of this MultipartFile with a new file handler. Useful when multiple processors need to read from the beginning of the file. Note: Both original and copied files must be closed to avoid resource leaks.
func (*MultipartFile) IsValid ¶ added in v1.7.1
func (f *MultipartFile) IsValid() bool
IsValid checks if the MultipartFile contains valid data. Returns false if any essential field is missing.
type MultipartFiles ¶
type MultipartFiles []MultipartFile
MultipartFiles is a collection of MultipartFile objects. Use this to receive all files from a multipart request:
type UploadRequest struct {
Files MultipartFiles `multipart:",allfiles"`
}
func (MultipartFiles) Close ¶
func (mf MultipartFiles) Close() error
Close closes all file handles in the collection to prevent resource leaks.
Example:
func handleUpload(w http.ResponseWriter, r *http.Request) {
var req UploadRequest
if err := roamer.Parse(r, &req); err != nil {
return
}
defer req.Files.Close() // Important: close all files
}
type MultipartFormData ¶
type MultipartFormData struct {
// contains filtered or unexported fields
}
MultipartFormData is a decoder for handling multipart form data (multipart/form-data), including file uploads and mixed form fields.
This decoder is specifically designed for handling HTML forms with file uploads, allowing simultaneous processing of regular form fields and uploaded files. It uses the "multipart" struct tag to map form fields to struct members.
File Upload Support ¶
- Single file uploads using *MultipartFile
- Multiple file uploads using MultipartFiles
- Access to file metadata (name, size, content-type)
- Automatic cleanup when files are properly closed
Special Tags ¶
- multipart:",allfiles" - Captures all uploaded files into a MultipartFiles slice
- multipart:"fieldname" - Maps to a specific form field or file input
Memory Management ¶
Files larger than maxMemory (default 32MB) are stored in temporary disk files. Smaller files are kept in memory. The threshold can be configured using WithMaxMemory().
Thread Safety ¶
The MultipartFormData decoder requires a structure cache set via SetStructureCache before use. After initialization, it is safe for concurrent use.
Important ¶
Always close uploaded files after processing to prevent resource leaks:
defer req.Files.Close()
func NewMultipartFormData ¶
func NewMultipartFormData(opts ...MultipartFormDataOptionsFunc) *MultipartFormData
NewMultipartFormData creates a decoder for multipart/form-data content, including file uploads. Handles both form fields and uploaded files.
Example:
// Default settings (32MB memory limit)
multipartDecoder := decoder.NewMultipartFormData()
// Custom memory limit
multipartDecoder := decoder.NewMultipartFormData(
decoder.WithMaxMemory(10 << 20), // 10 MB
)
// Example struct
type UploadRequest struct {
Title string `multipart:"title"`
Avatar *decoder.MultipartFile `multipart:"avatar"` // Single file
Gallery decoder.MultipartFiles `multipart:",allfiles"` // All files
}
func (*MultipartFormData) ContentType ¶
func (m *MultipartFormData) ContentType() string
ContentType returns the Content-Type header value that this decoder handles. For the MultipartFormData decoder, this is "multipart/form-data" by default. This method is used by the roamer package to match requests with the appropriate decoder.
func (*MultipartFormData) Decode ¶
func (m *MultipartFormData) Decode(r *http.Request, ptr any) error
Decode parses multipart form data into a struct pointer. Handles both form fields and file uploads.
Parameters:
- r: The HTTP request with multipart form data.
- ptr: Target struct pointer.
Returns:
- error: Error if decoding fails, nil if successful.
func (*MultipartFormData) SetStructureCache ¶ added in v1.12.0
func (m *MultipartFormData) SetStructureCache(cache *cache.Structure)
SetStructureCache assigns a structure cache to the decoder for improved performance. The cache stores precomputed field information to avoid reflection overhead on each request.
func (*MultipartFormData) Tag ¶ added in v1.12.1
func (m *MultipartFormData) Tag() string
Tag returns the struct tag name used for multipart field mapping. For the MultipartFormData decoder, this is "multipart" by default.
type MultipartFormDataOptionsFunc ¶
type MultipartFormDataOptionsFunc = func(*MultipartFormData)
MultipartFormDataOptionsFunc is a function type for configuring a MultipartFormData decoder. It follows the functional options pattern to provide a clean and extensible API.
func WithMaxMemory ¶
func WithMaxMemory(maxMemory int64) MultipartFormDataOptionsFunc
WithMaxMemory sets the maximum memory limit for parsing multipart data. Content beyond this limit will be stored in temporary files. Default is 32 MB.
Example: decoder.WithMaxMemory(10 << 20) // 10 MB
type XML ¶
type XML struct {
// contains filtered or unexported fields
}
XML is a decoder for handling XML request bodies. It uses the standard library's encoding/xml package for XML parsing.
func NewXML ¶
func NewXML(opts ...XMLOptionsFunc) *XML
NewXML creates an XML decoder for handling application/xml content. Uses standard library's encoding/xml package for XML parsing.
Example:
// Default XML decoder
xmlDecoder := decoder.NewXML()
// With custom Content-Type
xmlDecoder := decoder.NewXML(decoder.WithContentType("text/xml"))
// Example struct
type BookRequest struct {
Title string `xml:"title"`
Author string `xml:"author"`
}
func (*XML) ContentType ¶
ContentType returns the Content-Type header value that this decoder handles. For the XML decoder, this is "application/xml" by default. This method is used by the roamer package to match requests with the appropriate decoder.
type XMLOptionsFunc ¶
type XMLOptionsFunc = func(*XML)
XMLOptionsFunc is a function type for configuring an XML decoder. It follows the functional options pattern to provide a clean and extensible API.