Request Context

Request handling and context API

Overview

The Context type (often referred to as RequestContext) is passed to every handler and middleware. It provides access to the HTTP request, response helpers, parameter extraction, request binding, and context storage.

This is the central object for handling HTTP requests in Lokstra.

Import Path

import "github.com/primadi/lokstra/core/request"

// Or use the main package type alias
import "github.com/primadi/lokstra"

func handler(c *lokstra.RequestContext) error {
    // Use context
}

Type Definition

type Context struct {
    context.Context  // Embedded standard context
    
    // Helpers
    Req  *RequestHelper      // Request parameter extraction
    Resp *response.Response  // Response building
    Api  *response.ApiHelper // Opinionated API responses
    
    // Primitives (advanced usage)
    W *writerWrapper  // Response writer
    R *http.Request   // Raw HTTP request
}

Fields:


Context Methods

Next

Calls the next handler in the middleware chain.

Signature:

func (c *Context) Next() error

Returns:

Example:

func loggingMiddleware(c *lokstra.RequestContext) error {
    start := time.Now()
    
    // Call next handler
    err := c.Next()
    
    duration := time.Since(start)
    log.Printf("Request took %v", duration)
    
    return err
}

Use Cases:


Set

Stores a value in the request context.

Signature:

func (c *Context) Set(key string, value any)

Parameters:

Example:

// In middleware
func authMiddleware(c *lokstra.RequestContext) error {
    user := authenticateUser(c)
    c.Set("user", user)
    c.Set("user_id", user.ID)
    return c.Next()
}

// In handler
func getProfile(c *lokstra.RequestContext) error {
    user := c.Get("user").(*User)
    return c.Api.Success(user)
}

Get

Retrieves a value from the request context.

Signature:

func (c *Context) Get(key string) any

Parameters:

Returns:

Example:

userID := c.Get("user_id").(int)
user := c.Get("user").(*User)

// Safe retrieval
if val := c.Get("optional_key"); val != nil {
    data := val.(string)
}

SetContextValue

Stores a value in the standard Go context.

Signature:

func (c *Context) SetContextValue(key string, value any)

Parameters:

Example:

c.SetContextValue("request_id", requestID)
c.SetContextValue("trace_id", traceID)

Use Cases:


GetContextValue

Retrieves a value from the standard Go context.

Signature:

func (c *Context) GetContextValue(key string) any

Parameters:

Returns:

Example:

requestID := c.GetContextValue("request_id").(string)

RequestHelper (c.Req)

The RequestHelper provides methods for extracting request data.

Parameter Extraction

Param

Alias for PathParam - extracts path parameter.

Signature:

func (h *RequestHelper) Param(name string) string

Example:

// Route: GET /users/:id
id := c.Req.Param("id")

PathParam

Extracts path parameter with default value.

Signature:

func (h *RequestHelper) PathParam(name string, defaultValue string) string

Example:

// Route: GET /users/:id
id := c.Req.PathParam("id", "")
action := c.Req.PathParam("action", "view")

QueryParam

Extracts query parameter with default value.

Signature:

func (h *RequestHelper) QueryParam(name string, defaultValue string) string

Example:

// Request: GET /users?status=active&limit=10
status := c.Req.QueryParam("status", "all")
limit := c.Req.QueryParam("limit", "20")
page := c.Req.QueryParam("page", "1")

FormParam

Extracts form parameter with default value.

Signature:

func (h *RequestHelper) FormParam(name string, defaultValue string) string

Example:

// POST form data
username := c.Req.FormParam("username", "")
email := c.Req.FormParam("email", "")

HeaderParam

Extracts header value with default value.

Signature:

func (h *RequestHelper) HeaderParam(name string, defaultValue string) string

Example:

token := c.Req.HeaderParam("Authorization", "")
contentType := c.Req.HeaderParam("Content-Type", "application/json")
userAgent := c.Req.HeaderParam("User-Agent", "unknown")

Multiple Values

QueryParams

Extracts all values for a query parameter.

Signature:

func (h *RequestHelper) QueryParams(name string) []string

Example:

// Request: GET /search?tags=go&tags=web&tags=api
tags := c.Req.QueryParams("tags")
// tags = ["go", "web", "api"]

HeaderValues

Extracts all values for a header.

Signature:

func (h *RequestHelper) HeaderValues(name string) []string

Example:

acceptValues := c.Req.HeaderValues("Accept")
// acceptValues = ["application/json", "text/html"]

AllQueryParams

Returns all query parameters as a map.

Signature:

func (h *RequestHelper) AllQueryParams() map[string][]string

Example:

params := c.Req.AllQueryParams()
for key, values := range params {
    log.Printf("%s: %v", key, values)
}

AllHeaders

Returns all headers as a map.

Signature:

func (h *RequestHelper) AllHeaders() map[string][]string

Example:

headers := c.Req.AllHeaders()
for name, values := range headers {
    log.Printf("%s: %v", name, values)
}

Convenience Aliases

Query (Alias for QueryParam)

func (h *RequestHelper) Query(name string, defaultValue string) string

Header (Alias for HeaderParam)

func (h *RequestHelper) Header(name string) string

Form (Alias for FormParam)

func (h *RequestHelper) Form(name string, defaultValue string) string

Example:

// Shorter syntax
status := c.Req.Query("status", "all")
token := c.Req.Header("Authorization")
username := c.Req.Form("username", "")

Request Body

RawRequestBody

Returns the raw request body as bytes.

Signature:

func (h *RequestHelper) RawRequestBody() ([]byte, error)

Returns:

Example:

body, err := c.Req.RawRequestBody()
if err != nil {
    return err
}
log.Printf("Raw body: %s", string(body))

Notes:


BindJSON

Binds JSON request body to a struct.

Signature:

func (h *RequestHelper) BindJSON(v any) error

Parameters:

Returns:

Example:

type CreateUserInput struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"required,email"`
    Age   int    `json:"age" validate:"min=18"`
}

func createUser(c *lokstra.RequestContext) error {
    var input CreateUserInput
    if err := c.Req.BindJSON(&input); err != nil {
        return err // Auto-formatted as 400 Bad Request
    }
    
    // input is validated and ready to use
    user := saveUser(input)
    return c.Api.Created(user)
}

Automatic Features:


Binding Helpers

BindQuery

Binds query parameters to a struct.

Example:

type ListUsersFilter struct {
    Status string   `query:"status"`
    Tags   []string `query:"tags"`
    Limit  int      `query:"limit"`
    Page   int      `query:"page"`
}

func listUsers(c *lokstra.RequestContext) error {
    var filter ListUsersFilter
    if err := c.Req.BindQuery(&filter); err != nil {
        return err
    }
    
    users := queryUsers(filter)
    return c.Api.Success(users)
}

Struct Tags:


BindPath

Binds path parameters to a struct.

Example:

type UserParams struct {
    ID     string `path:"id" validate:"required"`
    Action string `path:"action"`
}

func userAction(c *lokstra.RequestContext) error {
    var params UserParams
    if err := c.Req.BindPath(&params); err != nil {
        return err
    }
    
    // params.ID and params.Action are populated
    return handleAction(params)
}

BindHeader

Binds headers to a struct.

Example:

type RequestHeaders struct {
    Authorization string   `header:"Authorization" validate:"required"`
    ContentType   string   `header:"Content-Type"`
    Accept        []string `header:"Accept"`
}

func handler(c *lokstra.RequestContext) error {
    var headers RequestHeaders
    if err := c.Req.BindHeader(&headers); err != nil {
        return err
    }
    
    token := headers.Authorization
    // ...
}

Complete Examples

Basic Parameter Extraction

func getUser(c *lokstra.RequestContext) error {
    // Path parameter
    id := c.Req.Param("id")
    
    // Query parameters
    fields := c.Req.Query("fields", "")
    includeDeleted := c.Req.Query("include_deleted", "false")
    
    // Headers
    token := c.Req.Header("Authorization")
    
    user, err := fetchUser(id, fields, includeDeleted == "true")
    if err != nil {
        return c.Api.NotFound("User not found")
    }
    
    return c.Api.Success(user)
}

JSON Request Binding

type UpdateUserInput struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"required,email"`
    Age   int    `json:"age" validate:"omitempty,min=18,max=120"`
}

func updateUser(c *lokstra.RequestContext) error {
    id := c.Req.Param("id")
    
    var input UpdateUserInput
    if err := c.Req.BindJSON(&input); err != nil {
        return err // Auto-formatted as validation error
    }
    
    user, err := updateUserInDB(id, input)
    if err != nil {
        return c.Api.InternalError("Failed to update user")
    }
    
    return c.Api.Success(user)
}

Middleware with Context Storage

func authMiddleware(c *lokstra.RequestContext) error {
    token := c.Req.Header("Authorization")
    if token == "" {
        return c.Api.Unauthorized("Missing authorization token")
    }
    
    user, err := validateToken(token)
    if err != nil {
        return c.Api.Unauthorized("Invalid token")
    }
    
    // Store user in context
    c.Set("user", user)
    c.Set("user_id", user.ID)
    c.SetContextValue("user_id", user.ID)
    
    return c.Next()
}

func getProfile(c *lokstra.RequestContext) error {
    // Retrieve user from context
    user := c.Get("user").(*User)
    return c.Api.Success(user)
}

func deleteAccount(c *lokstra.RequestContext) error {
    userID := c.Get("user_id").(int)
    if err := deleteUserAccount(userID); err != nil {
        return err
    }
    return c.Api.NoContent()
}

Query Parameter Binding

type SearchFilter struct {
    Query    string   `query:"q" validate:"required"`
    Tags     []string `query:"tags"`
    Category string   `query:"category"`
    Limit    int      `query:"limit" validate:"min=1,max=100"`
    Page     int      `query:"page" validate:"min=1"`
}

func search(c *lokstra.RequestContext) error {
    var filter SearchFilter
    if err := c.Req.BindQuery(&filter); err != nil {
        return err
    }
    
    // Set defaults
    if filter.Limit == 0 {
        filter.Limit = 20
    }
    if filter.Page == 0 {
        filter.Page = 1
    }
    
    results := performSearch(filter)
    return c.Api.Success(results)
}

File Upload Handling

func uploadFile(c *lokstra.RequestContext) error {
    // Parse multipart form
    if err := c.R.ParseMultipartForm(10 << 20); err != nil { // 10MB
        return c.Api.BadRequest("Failed to parse form")
    }
    
    file, header, err := c.R.FormFile("file")
    if err != nil {
        return c.Api.BadRequest("No file uploaded")
    }
    defer file.Close()
    
    // Save file
    filename := header.Filename
    savedPath, err := saveUploadedFile(file, filename)
    if err != nil {
        return c.Api.InternalError("Failed to save file")
    }
    
    return c.Api.Success(map[string]string{
        "filename": filename,
        "path":     savedPath,
    })
}

Complex Request Processing

func complexHandler(c *lokstra.RequestContext) error {
    // Path parameters
    id := c.Req.Param("id")
    
    // Query parameters
    includeRefs := c.Req.Query("include_refs", "false") == "true"
    fields := c.Req.QueryParams("fields")
    
    // Headers
    token := c.Req.Header("Authorization")
    acceptLang := c.Req.Header("Accept-Language")
    
    // Request body
    var input UpdateInput
    if err := c.Req.BindJSON(&input); err != nil {
        return err
    }
    
    // Context values
    userID := c.Get("user_id").(int)
    
    // Process
    result, err := processComplexRequest(id, input, fields, includeRefs, userID, acceptLang)
    if err != nil {
        return c.Api.InternalError(err.Error())
    }
    
    return c.Api.Success(result)
}

See Also