portainer/pkg/libhttp/request/request.go

206 lines
6.8 KiB
Go

// Package request provides function to retrieve content from a *http.Request object of the net/http standard library,
// be it JSON body payload, multi-part form values or query parameters.
// It also provides functions to retrieve route variables when using gorilla/mux.
package request
import (
"errors"
"fmt"
"io"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/segmentio/encoding/json"
)
const (
// ErrInvalidQueryParameter defines the message of an error raised when a mandatory query parameter has an invalid value.
ErrInvalidQueryParameter = "invalid query parameter"
// ErrInvalidRequestURL defines the message of an error raised when the data sent in the query or the URL is invalid
ErrInvalidRequestURL = "invalid request URL"
// ErrMissingQueryParameter defines the message of an error raised when a mandatory query parameter is missing.
ErrMissingQueryParameter = "missing query parameter"
// ErrMissingFormDataValue defines the message of an error raised when a mandatory form data value is missing.
ErrMissingFormDataValue = "missing form data value"
)
// RetrieveMultiPartFormFile returns the content of an uploaded file (form data) as bytes as well
// as the name of the uploaded file.
func RetrieveMultiPartFormFile(request *http.Request, requestParameter string) ([]byte, string, error) {
file, headers, err := request.FormFile(requestParameter)
if err != nil {
return nil, "", err
}
defer file.Close()
fileContent, err := io.ReadAll(file)
if err != nil {
return nil, "", err
}
return fileContent, headers.Filename, nil
}
// RetrieveMultiPartFormJSONValue decodes the value of some form data as a JSON object into the target parameter.
// If optional is set to true, will not return an error when the form data value is not found.
func RetrieveMultiPartFormJSONValue(request *http.Request, name string, target any, optional bool) error {
value, err := RetrieveMultiPartFormValue(request, name, optional)
if err != nil {
return err
} else if value == "" {
return nil
}
return json.Unmarshal([]byte(value), target)
}
// RetrieveMultiPartFormValue returns the value of some form data as a string.
// If optional is set to true, will not return an error when the form data value is not found.
func RetrieveMultiPartFormValue(request *http.Request, name string, optional bool) (string, error) {
value := request.FormValue(name)
if value == "" && !optional {
return "", errors.New(ErrMissingFormDataValue)
}
return value, nil
}
// RetrieveNumericMultiPartFormValue returns the value of some form data as an integer.
// If optional is set to true, will not return an error when the form data value is not found.
func RetrieveNumericMultiPartFormValue(request *http.Request, name string, optional bool) (int, error) {
value, err := RetrieveMultiPartFormValue(request, name, optional)
if err != nil {
return 0, err
}
return strconv.Atoi(value)
}
// RetrieveBooleanMultiPartFormValue returns the value of some form data as a boolean.
// If optional is set to true, will not return an error when the form data value is not found.
func RetrieveBooleanMultiPartFormValue(request *http.Request, name string, optional bool) (bool, error) {
value, err := RetrieveMultiPartFormValue(request, name, optional)
if err != nil {
return false, err
}
return value == "true", nil
}
// RetrieveRouteVariableValue returns the value of a route variable as a string.
func RetrieveRouteVariableValue(request *http.Request, name string) (string, error) {
routeVariables := mux.Vars(request)
if routeVariables == nil {
return "", errors.New(ErrInvalidRequestURL)
}
routeVar := routeVariables[name]
if routeVar == "" {
return "", errors.New(ErrInvalidRequestURL)
}
return routeVar, nil
}
// RetrieveNumericRouteVariableValue returns the value of a route variable as an integer.
func RetrieveNumericRouteVariableValue(request *http.Request, name string) (int, error) {
routeVar, err := RetrieveRouteVariableValue(request, name)
if err != nil {
return 0, err
}
return strconv.Atoi(routeVar)
}
// RetrieveQueryParameter returns the value of a query parameter as a string.
// If optional is set to true, will not return an error when the query parameter is not found.
func RetrieveQueryParameter(request *http.Request, name string, optional bool) (string, error) {
queryParameter := request.FormValue(name)
if queryParameter == "" && !optional {
return "", errors.New(ErrMissingQueryParameter)
}
return queryParameter, nil
}
// RetrieveNumericQueryParameter returns the value of a query parameter as an integer.
// If optional is set to true, will not return an error when the query parameter is not found.
func RetrieveNumericQueryParameter(request *http.Request, name string, optional bool) (int, error) {
queryParameter, err := RetrieveQueryParameter(request, name, optional)
if err != nil {
return 0, err
} else if queryParameter == "" && optional {
return 0, nil
}
return strconv.Atoi(queryParameter)
}
// RetrieveBooleanQueryParameter returns the value of a query parameter as a boolean.
// If optional is set to true, will not return an error when the query parameter is not found.
func RetrieveBooleanQueryParameter(request *http.Request, name string, optional bool) (bool, error) {
queryParameter, err := RetrieveQueryParameter(request, name, optional)
if err != nil {
return false, err
}
return queryParameter == "true", nil
}
// RetrieveJSONQueryParameter decodes the value of a query parameter as a JSON object into the target parameter.
// If optional is set to true, will not return an error when the query parameter is not found.
func RetrieveJSONQueryParameter(request *http.Request, name string, target any, optional bool) error {
queryParameter, err := RetrieveQueryParameter(request, name, optional)
if err != nil {
return err
} else if queryParameter == "" {
return nil
}
return json.Unmarshal([]byte(queryParameter), target)
}
// RetrieveArrayQueryParameter returns the value of a query parameter as a string array.
// it will return nil if the query parameter is not found.
//
// Example:
//
// GET /api/resource?filter=foo&filter=bar
// RetrieveArrayQueryParameter(request, "filter") => []string{"foo", "bar"}
func RetrieveArrayQueryParameter(r *http.Request, parameter string) []string {
list, exists := r.Form[parameter+"[]"]
if !exists {
return nil
}
return list
}
func RetrieveNumberArrayQueryParameter[T ~int](r *http.Request, parameter string) ([]T, error) {
if r.Form == nil {
err := r.ParseForm()
if err != nil {
return nil, fmt.Errorf("Unable to parse form: %w", err)
}
}
list := RetrieveArrayQueryParameter(r, parameter)
if list == nil {
return nil, nil
}
var result []T
for _, item := range list {
number, err := strconv.Atoi(item)
if err != nil {
return nil, fmt.Errorf("Unable to parse parameter %q: %w", parameter, err)
}
result = append(result, T(number))
}
return result, nil
}