Example using httpx

Idea

  • The cache server will be changed into a JSON document cache server
  • Each entry contains an ID and a raw content
  • The raw content is a JSON document itself
  • Here Go provides the type json.RawMessage

Example

The JSON cache server package

package jsoncache

import (
    "encoding/json"
    "net/http"
    "sync"

    "./pkg/httpx"
)

const (
    // Prefix for API calls.
    apiPrefix = apiPrefix

    // Name of the jsoncache resource.
    resourceName = "jsoncache"
)

// jsonDoc describes one entry in the cache server. It's a wrapper
// containing the ID and the raw JSON document. It's the content of
// the map.
type jsonDoc struct {
    ID      string          `json:"id"`
    Content json.RawMessage `json:"content"`
}

// Handler provides a simple JSON in-memory cache server. Cache is 
// done via a map of string to jsonDoc. The jsonDoc contains the ID and a
// raw content. The sync.RWMutex is used to ensure that the cache is
// thread-safe.
type Handler struct {
    mu    sync.RWMutex
    cache map[string]jsonDoc
}

// NewHandler creates the cache server. It's simply needed to create
// the map of string to jsonDoc.
func NewHandler() *Handler {
    return &Handler{
        cache: make(map[string]jsonDoc),
    }
}

// ServeHTTPGet retrieves a JSON document out of cache.
func (h *Handler) ServeHTTPGet(w http.ResponseWriter, r *http.Request) {
    h.mu.RLock()
    defer h.mu.RUnlock()

    ress := httpx.PathToResources(r, apiPrefix)
    if ress == nil {
        http.Error(w, "resource and ID are missing", http.StatusInvalidRequest)
        return
    }
    if !ress.IsPath(resourceName) {
        http.Error(w, "resource is not json-cache", http.StatusBadRequest)
        return
    }
    doc, ok := h.cache[ress.PathID(resourceName)]
    if !ok {
        http.Error(w, "JSON document not found", http.StatusNotFound)
        return
    }
    err := https.WriteBody(w, httpx.ContentTypeJSON, doc) 
    if err != nil {
        log.Printf("Error writing body: %v", err)
    }
}

// ServeHTTPPost adds a new JSON document to the cache.
func (h *Handler) ServeHTTPPost(w http.ResponseWriter, r *http.Request) {
    h.mu.Lock()
    defer h.mu.Unlock()

    if !httpx.PathToResources(apiPrefix).IsPath(resourceName) {
        http.Error(w, "missing or bad resource", http.StatusBadRequest)
        return
    }
    var doc jsonDoc
    err := httpx.ReadBody(r, &doc)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    _, ok := h.cache[doc.ID]
    if ok {
        http.Error(w, "JSON document already exists", http.StatusConflict)
        return
    }
    h.cache[doc.ID] = doc
    w.WriteHeader(http.StatusCreated)
}

Using the JSON cache server

package main

import (
    "log"
    "net/http"

    "./pkg/httpx"
    "./pkg/jsoncache"
)

// main runs the cache server.
func main() {
    h := jsoncache.NewHandler()
    mh := httpx.NewMethodHandler(h)
    err := http.ListenAndServe(":8080", mh)

    if err != nil {
        log.Fatal(err)
    }
}