httptransport

package
v1.4.4 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

README

API Endpoint Parameter Binding

This package provides a powerful parameter binding system for API endpoints that automatically extracts and validates request data from multiple sources.

Overview

The handler converter supports binding request parameters from multiple sources using struct tags:

  • Path parameters from URL segments like /users/{id}
  • Query parameters from URL query strings like ?page=1&limit=10
  • Header parameters from HTTP headers like Authorization: Bearer <token>
  • Cookie parameters from HTTP cookies with automatic fallback support
  • JSON body parameters from request body data
  • Raw body bytes for endpoints that must verify a signature over the exact payload
Example
type ComplicatedRequest struct {
    Token     string   `header:"Authorization" scheme:"Bearer"`   // Authorization: Bearer <token>
    SessionID string   `cookie:"session_id" validate:"required"`  // Only from cookie
    Name      string   `json:"name" validate:"required"`
    Email     string   `json:"email" validate:"required,email"`
    Age       int      `json:"age" validate:"min=18"`
    UserID    string   `path:"id"`                                // Binds to {id} in URL path
    Page      int      `query:"page" default:"1"`                 // ?page=1
    Limit     int      `query:"limit" default:"20"`               // ?limit=10
}

Parameter Binding Examples

1. Path Parameters

Extract values from URL path segments:

Route: /v1/users/{id}/posts/{postId}

type GetUserPostRequest struct {
    UserID  string `path:"id"`      // Binds to {id} in URL path
    PostID  int    `path:"postId"`  // Binds to {postId} in URL path
}
2. Query Parameters

Handle URL query strings with various data types:

URL: /v1/users?page=1&limit=10&tags=go,api&active=true

type ListUsersRequest struct {
    Page    int      `query:"page" default:"1"`           // ?page=1
    Limit   int      `query:"limit" default:"20"`         // ?limit=10
    Tags    []string `query:"tags"`                       // ?tags=go,api (comma-separated or repeated)
    Active  bool     `query:"active"`                     // ?active=true
    Search  string   `query:"search"`                     // ?search=term
}
3. Header Parameters

Extract values from HTTP headers:

type AuthRequest struct {
    Token     string `header:"Authorization" scheme:"Bearer"`  // Authorization: Bearer <token>
    UserAgent string `header:"User-Agent"`                    // User-Agent: <value>
    APIKey    string `header:"X-API-Key"`                     // X-API-Key: <value>
}
Flexible Authorization Schemes

For Authorization headers, you can support multiple schemes (Bearer or Basic) without specifying a scheme:

type RefreshTokenRequest struct {
    // Supports both "Bearer <token>" and "Basic <base64(token:)>" formats
    RefreshToken string `header:"Authorization" validate:"required"`
}

When scheme is omitted, the binding system automatically validates and extracts tokens from either Bearer or Basic auth schemes.

Extract values from HTTP cookies with automatic fallback support:

type RefreshTokenRequest struct {
    // Tries Authorization header first, falls back to cookie if header is missing or invalid
    RefreshToken string `header:"Authorization" cookie:"__Secure-openmrp.refresh-token" validate:"required"`
}

Cookie Binding Behavior:

  • If both header and cookie tags are present, the header is checked first
  • If the header is missing or invalid, the system automatically falls back to the cookie
  • For Authorization headers, supports both Bearer and Basic schemes when no scheme is specified
  • Cookie fallback also works when Authorization header validation fails

Example with cookie-only binding:

type CookieOnlyRequest struct {
    SessionID string `cookie:"session_id" validate:"required"`  // Only from cookie
}
5. JSON Body Parameters

Handle request body data with validation:

type CreateUserRequest struct {
    Name     string `json:"name" validate:"required"`
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"min=18"`
    Metadata map[string]interface{} `json:"metadata,omitempty"`
}
6. Raw Body

Webhook endpoints need the exact bytes the sender signed, before any JSON decoding. Tag a []byte field with rawbody:

type StripeWebhookRequest struct {
    Signature string `header:"Stripe-Signature" validate:"required"`
    Payload   []byte `rawbody:"true"`
}

The tag only works on []byte fields, the body is read once and shared across every rawbody field on the struct, and the read is capped at 1MB. The tag's value is not interpreted — any non-empty value works, and "true" is the convention.

7. Combined Example

A realistic example showing all parameter types together:

Route: /v1/users/{userId}/posts?page=1&limit=10
Headers: Authorization: Bearer <token>
Body: {"title": "My Post", "content": "..."}

type CreatePostRequest struct {
    // Path parameters
    UserID string `path:"userId" validate:"required"`

    // Query parameters
    Page   int `query:"page" default:"1"`
    Limit  int `query:"limit" default:"20"`

    // Header parameters
    Token string `header:"Authorization" scheme:"Bearer" validate:"required"`

    // JSON body parameters
    Title   string `json:"title" validate:"required,min=1,max=100"`
    Content string `json:"content" validate:"required"`
}
8. Time Parsing

Handle date/time parameters with custom layouts:

type TimeRequest struct {
    CreatedAt time.Time `query:"created_at" time_layout:"2006-01-02"`  // ?created_at=2023-12-25
    UpdatedAt time.Time `query:"updated_at"`                           // Uses RFC3339 by default
}
9. Default Values

Provide fallback values when parameters are missing:

type OptionalRequest struct {
    Page     int    `query:"page" default:"1"`           // Defaults to 1 if not provided
    Sort     string `query:"sort" default:"created_at"`  // Defaults to "created_at"
    Include  bool   `query:"include" default:"true"`     // Defaults to true
}

Binding Precedence

Parameters are bound in the following order (later sources override earlier ones):

  1. Headers - HTTP header values
  2. Cookies - HTTP cookie values (used as fallback when header is missing or invalid)
  3. Path parameters - URL path segments
  4. Query parameters - URL query string values
  5. JSON body - Request body data (for POST/PUT/PATCH requests)

Note: When both header and cookie tags are present on the same field, the header takes precedence. The cookie is only used if the header is missing or fails validation.

Validation

All fields are automatically validated after binding:

  • Use validate:"required,email,min=5" tags for validation rules
  • Validation errors are automatically converted to user-friendly API error responses
  • The validation system supports all standard validator tags

Supported Data Types

The binding system supports automatic conversion for:

  • string - Direct string values
  • int, int8, int16, int32, int64 - Integer values
  • uint, uint8, uint16, uint32, uint64 - Unsigned integer values
  • float32, float64 - Floating point values
  • bool - Boolean values (true/false, 1/0, etc.)
  • time.Time - Date/time values with custom layouts
  • []string - String arrays (for query parameters)
  • map[string]interface{} - JSON objects

Struct Tags Reference

Tag Description Example
path:"name" Binds to URL path parameter {name} path:"userId"
query:"name" Binds to query parameter ?name=value query:"page"
header:"Name" Binds to HTTP header Name: value header:"Authorization"
cookie:"name" Binds to HTTP cookie name cookie:"session_id"
scheme:"Bearer" Strips scheme prefix from header value scheme:"Bearer"
json:"name" Binds to JSON field in request body json:"title"
rawbody:"true" Binds the raw request body to []byte rawbody:"true"
default:"value" Fallback value if parameter is missing default:"1"
time_layout:"layout" Custom time parsing layout time_layout:"2006-01-02"
validate:"rules" Validation rules for the field validate:"required,email"

Cookie Fallback: When both header and cookie tags are present, the header is checked first. If the header is missing or invalid, the system automatically falls back to the cookie value.

Error Handling

When validation fails, the system automatically:

  1. Converts validation errors to user-friendly messages
  2. Returns appropriate HTTP status codes (400 for validation errors)
  3. Formats errors as JSON responses using the apierror.APIErrorResponse envelope

Unknown fields are rejected rather than ignored: an unrecognized JSON body field or query parameter produces a validation error naming the offending field.

Example error response:

{
    "error": {
        "code": "validation_failed",
        "type": "invalid_request_error",
        "message": "Title must be at least 1 characters long",
        "param": "title",
        "doc_url": "https://docs.openmrp.ai/api-reference/errors#validation_failed",
        "is_transient": false,
        "quota": null,
        "request_log_url": "https://openmrp.ai/dashboard/request-logs/rq_fbv1ygmybo3eauykr74"
    }
}

Every field is always present — param, doc_url, quota, and request_log_url are null when they do not apply. The shape is defined by apierror.ResponseError in shared/errors.

Documentation

Index

Constants

View Source
const (
	AttrHTTPStatusCode = "http.status_code"
	AttrErrorType      = "error.type"
)

Variables

View Source
var PathExtractor func(*http.Request) func(string) string = defaultPathExtractor

Functions

func AllocIfPtr

func AllocIfPtr[T any](x T) T

func BindFromHeaders

func BindFromHeaders(r *http.Request, dst any) error

func BindFromPath

func BindFromPath(r *http.Request, dst any) error

func BindFromQuery

func BindFromQuery(u *url.URL, dst any) error

func BindIncomingRequest

func BindIncomingRequest(r *http.Request, dst any, allowIncludeQueryKeys bool) error

BindIncomingRequest binds header, path, and query parameters with one traversal of the cached bind plan. For each field that declares binding tags it applies headers/cookies first, path second, and query third, matching BindFromHeaders + BindFromPath + BindFromQuery on the same destination.

func BindRawBody

func BindRawBody(r *http.Request, dst any) error

func DecodeJSONInto

func DecodeJSONInto(dst any, r *http.Request, disallowUnknown bool) error

func GetIdentity

func GetIdentity(ctx context.Context) (*types.Identity, *apierror.APIError)

func RejectUnknownQueryParams

func RejectUnknownQueryParams(u *url.URL, dst any, allowInclude bool) *apierror.APIError

RejectUnknownQueryParams returns an error when the URL contains query keys that are not declared on the request struct (via `query` tags). Slice parameters accept either ?key= or ?key[]= shapes; both key forms are treated as allowed. When allowInclude is true, include and include[] are permitted (validated separately by the endpoint).

func RespondWithAPIError

func RespondWithAPIError(ctx context.Context, w http.ResponseWriter, apiErr *apierror.APIError, opts ...RespondOption)

func RespondWithFile

func RespondWithFile(ctx context.Context, w http.ResponseWriter, code int, fd *FileDownload, opts ...RespondOption)

RespondWithFile writes a file download response.

func RespondWithJSON

func RespondWithJSON(ctx context.Context, w http.ResponseWriter, code int, payload any, opts ...RespondOption)

func SetFrontendURL

func SetFrontendURL(url string)

SetFrontendURL configures the frontend base URL used to build request log links in error responses. When empty, the request_log_url field will be null.

func ShouldDecodeBody

func ShouldDecodeBody(r *http.Request) bool

func ValidateEnumFields

func ValidateEnumFields(dst any) *apierror.APIError

Types

type FileDownload

type FileDownload struct {
	ContentType string
	Filename    string
	Body        []byte
}

FileDownload is a response type for endpoints that return a file (e.g. Excel export). When the service returns *FileDownload, the handler writes the body with Content-Type and Content-Disposition.

func (*FileDownload) SchemaExample

func (*FileDownload) SchemaExample() any

type RespondOption

type RespondOption func(http.Header)

func WithHeader

func WithHeader(key, value string) RespondOption

func WithLocation

func WithLocation(path string) RespondOption

Jump to

Keyboard shortcuts

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