Skip to content

Request Helpers

The contract/request package provides convenience functions for extracting data from *http.Request. These are thin wrappers over Go’s standard library that reduce boilerplate and provide consistent patterns with default value support.

Extract path parameters set by the router’s {name} patterns:

import "github.com/studiolambda/cosmos/contract/request"
func getUser(w http.ResponseWriter, r *http.Request) error {
id := request.Param(r, "id")
// ...
}

Use ParamOr to provide a default value when the parameter is missing or empty:

page := request.ParamOr(r, "page", "1")

Read URL query string values:

search := request.Query(r, "q") // returns "" if not present
limit := request.QueryOr(r, "limit", "20") // returns "20" if not present

Check if a query parameter exists (even if empty):

if request.HasQuery(r, "verbose") {
// ?verbose or ?verbose= was in the URL
}

HasQuery distinguishes between a missing parameter and one present with an empty value. QueryOr only returns the default when the parameter does not exist at all — if it exists with an empty value, the empty string is returned.

Read request headers:

contentType := request.Header(r, "Content-Type")
token := request.HeaderOr(r, "Authorization", "none")

Check if a header is present and non-empty:

if request.HasHeader(r, "X-Request-ID") {
// header exists with a non-empty value
}

Get all values for a multi-value header:

acceptValues := request.HeaderValues(r, "Accept")

Read cookies from the request:

cookie := request.Cookie(r, "session_id") // returns *http.Cookie or nil
value := request.CookieValue(r, "session_id") // returns the value string or ""

With a default value:

theme := request.CookieValueOr(r, "theme", "dark")

Read the raw request body:

bytes, err := request.Bytes(r)
text, err := request.String(r)

Decode JSON or XML into a typed value using generics:

type CreateUserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
}
func createUser(w http.ResponseWriter, r *http.Request) error {
body, err := request.JSON[CreateUserRequest](r)
if err != nil {
return err
}
// body is of type CreateUserRequest
// ...
}

XML works the same way:

type Order struct {
XMLName xml.Name `xml:"order"`
ID string `xml:"id,attr"`
Total float64 `xml:"total"`
}
order, err := request.XML[Order](r)

Both JSON and XML use streaming decoders for memory efficiency. The request body is consumed on first read and cannot be read again.

Access the session from the request context (requires the session middleware):

session, ok := request.Session(r)
if !ok {
// no session middleware configured
}
value, exists := session.Get("user_id")

Use MustSession when you know the session middleware is present — it panics with a Problem Details error if it isn’t:

session := request.MustSession(r)
session.Put("user_id", "123")

For applications using multiple session stores with different context keys:

session, ok := request.SessionKeyed(r, myCustomKey)
session := request.MustSessionKeyed(r, myCustomKey)

Access the lifecycle hooks from the request context:

hooks := request.Hooks(r)
hooks.AfterResponse(func(err error) {
// cleanup logic
})

This panics if the hooks context is not present. In the framework, hooks are always available because the handler’s ServeHTTP method injects them automatically.