urlvalues

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 12, 2022 License: Apache-2.0 Imports: 8 Imported by: 0

README

urlvalues

Copyright 2022, Johan Rönkkö

Go package for unmarshalling URL values (typically query parameters and form values) into struct values. Below illustrates how one might use this package to unmarshall and validate query paramaters in a HTTP handler.

func (h Handler) ListNicknames(w http.ResponseWriter, r *http.Request) {
	var params struct {
		Filter   []string  `urlvalue:"filter"`
		Until    time.Time `urlvalue:"until,default:now,layout:RFC822"`
		Since    time.Time `urlvalue:"since,default:now-3m+15d-1y,layout:2006-01-02""`
		IsActive *bool     `urlvalue:"is_active"`
	}
	if err := urlvalues.Unmarshal(r.URL.Query(), &params); err != nil {
		var parseErr *urlvalues.ParseError
		switch {
			case errors.As(err, &parseErr):
				http.Error(w, parseErr.Error(), http.StatusBadRequest)
			default:
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return
	}
	// ...
}

Install

Install by running

go get github.com/nahojer/urlvalues

Or just copy the souce code into your project.

Documentation

All of the documentation can be found on the go.dev website. [TODO: link to documentation]

Licensing

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Credits

A special thanks to Ardan Labs whose conf project inspired the field extracting and processing implementation of this project.

Documentation

Overview

Package urlvalues unmarshals URL values as defined by net/url into struct values.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInvalidStruct = errors.New("urlvalues: target must be a struct pointer")

ErrInvalidStruct indicates that the Unmarshal target is not of correct type.

Functions

func Unmarshal

func Unmarshal(data url.Values, v any, setParseOpts ...SetParseOptionFunc) error

Unmarshal unmarshals data into the value pointed to by v. If v is nil or not a struct pointer, Unmarshal returns an ErrInvalidStruct error. If data is nil, Unmarshal returns without updating v's fields.

Slices are decoded by splitting URL values by a delimiter and parsing each item individually. The delimiter defaults to semicolon (;), but can by customized by passing the WithDelimiter SetParseOptionFunc. Key-value pairs of maps are split using the same delimiter. Keys and their values are separated by a colon (:), with the key to the left and the value to the right of the colon.

Fields with types implementing encoding.TextUnmarshaller and/or encoding.BinaryUnmarshaller will be decoded using those interfaces, respectively. If a type implements both interfaces, the encoding.TextUnmarshaller interface is used to decode the URL value.

The decoding of each struct field can be customized by the name string stored under the "urlvalue" key in the struct field's tag. The name string acts as a key into data, possibly followed by a comma-separated list of options. The name may be empty, in which case the field name of the struct will act as as key into data in its stead.

The "default" option allows for setting a default value on a field in case corresponding URL value is not present in data, or if the value is the zero value for the field's type.

The "layout" option only applies to fields of type time.Time and allows for customizing how URL values should be parsed by providing layouts understood by time.Parse.

As a special case, if the field tag is "-", the field is always omitted. Note that a field with name "-" can still be generated using the tag "-,".

Examples of struct field tags and their meanings:

// Field defaults to 42.
Field int `urlvalue:"myName,default:42"`

// Field is parsed using the RFC850 layout.
Field time.Time `urlvalue:"myName,layout:RFC850"`

// Field is ignored by this package.
Field int `urlvalue:"-"`

// Field appears in URL values with key "-".
Field int `urlvalue:"-,"`

// Field is decoded as time.Now().AddDate(3, -4, 9).
Field time.Time `urlvalue:"myName,default:now+3y-4m+9d"`

The parsing of time.Time fields is extended to support a "now" based parsing. It parses the URL value "now" to time.Now(). Furthermore, it extends this syntax by allowing the consumer to subtract or add days (d), months (m) and years (y) to "now". This is done by prepending the date identifiers (d,m,y) with a minus (-) or plus (+) sign.

Any error that occurs while processing struct fields results in a FieldError. ParseError wraps around FieldError and is returned if any error occurs while parsing the data that was passed into Unmarshal. ParseError is never returned from errors occuring while parsing default values.

Example
package main

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/nahojer/urlvalues"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "http://localhost?since=now-3m&directors=Quentin%20Tarantino&directors=Christopher%20Nolan", nil)
	if err != nil {
		panic(err)
	}

	var params struct {
		Since     time.Time `urlvalue:"since,default:now-3m,layout=2006-01-02"`
		Until     time.Time `urlvalue:"until,default:now,layout=2006-01-02"`
		Genres    []string  `urlvalue:"genres,default:action;drama"`
		Directors []string  `urlvalue:"directors"`
	}
	if err := urlvalues.Unmarshal(req.URL.Query(), &params); err != nil {
		panic(err)
	}

	fmt.Printf("Genres: %s\n", strings.Join(params.Genres, ", "))
	fmt.Printf("Directors: %s\n", strings.Join(params.Directors, ", "))
}
Output:

Genres: action, drama
Directors: Quentin Tarantino, Christopher Nolan

Types

type FieldError

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

FieldError occurs when a URL value failed to be decoded into a struct field.

func (*FieldError) Error

func (err *FieldError) Error() string

type ParseError

type ParseError struct {
	// Name of struct field.
	FieldName string
	// Key into URL values.
	Key string
	// contains filtered or unexported fields
}

ParseError occurs when a URL value failed to be parsed into a struct field's type. It exposes the field name and the key used to extract the URL value. Internally, it holds the FieldError, which can be accessed by unwrapping the error.

func (*ParseError) Error

func (e *ParseError) Error() string

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

Unwrap returns the underlying FieldError.

type ParseOptions

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

ParseOptions holds all the options that allows for customizing the parsing behaviour when unmarshalling URL values.

func (*ParseOptions) Delim

func (o *ParseOptions) Delim() string

Delim returns the delimiter used to convert slices and maps from and into their string representation. Defaults to semicolon (;) if not set or set to the empty string.

type SetParseOptionFunc

type SetParseOptionFunc func(*ParseOptions)

SetParseOptionFunc allows for overriding parsing behaviour when unmarshalling URL values.

func WithDelimiter

func WithDelimiter(s string) SetParseOptionFunc

WithDelimiter returns a SetParseOptionFunc that sets the delimiter used to convert slices and maps from and into their string representation.

Jump to

Keyboard shortcuts

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