clojure-ring-core-middleware
Ring core middleware for Clojure. Use when working with parameter parsing, sessions, cookies, flash messages, static files, or composing middleware stacks. Triggers on ring.middleware, wrap-params, wrap-session, wrap-cookies, or middleware composition questions.
When & Why to Use This Skill
This Claude skill provides comprehensive support for Clojure Ring core middleware, enabling developers to efficiently manage HTTP request processing and web server configurations. It covers essential web development tasks including parameter parsing (URL, form, and multipart), session and cookie management, flash messages for redirects, and serving static resources. By providing guidance on middleware composition and best practices, it simplifies the creation of robust, secure, and high-performance Clojure web applications and APIs.
Use Cases
- Case 1: Implementing secure user authentication and state management using wrap-session and wrap-cookies with encrypted cookie stores.
- Case 2: Processing complex web forms and handling multi-part file uploads by correctly configuring wrap-params and wrap-multipart-params middleware.
- Case 3: Optimizing web application performance by serving static assets through wrap-resource combined with wrap-content-type and wrap-not-modified for browser caching.
- Case 4: Building a standardized middleware stack for development or production environments to ensure consistent request handling and security headers.
- Case 5: Managing temporary user notifications and feedback across HTTP redirects using the wrap-flash middleware.
| name | clojure-ring-core-middleware |
|---|---|
| description | | |
Ring Core Middleware
Ring middleware are higher-order functions that wrap handlers to add functionality. Middleware compose via function wrapping - outermost middleware executes first.
Quick Start
Common middleware stack:
(require '[ring.middleware.params :refer [wrap-params]]
'[ring.middleware.keyword-params :refer [wrap-keyword-params]]
'[ring.middleware.session :refer [wrap-session]]
'[ring.middleware.flash :refer [wrap-flash]])
(def app
(-> handler
wrap-keyword-params
wrap-params
wrap-flash
wrap-session))
Order matters: innermost (handler) to outermost (first to execute).
Parameter Processing
wrap-params
Parses URL-encoded parameters from query string and form body. Adds :query-params, :form-params, :params to request.
(wrap-params handler {:encoding "UTF-8"})
;; URL: /search?q=clojure -> {:params {"q" "clojure"}}
wrap-keyword-params
Converts parameter string keys to keywords. Must come after wrap-params.
(-> handler wrap-keyword-params wrap-params)
;; {"q" "clojure"} -> {:q "clojure"}
wrap-nested-params
Converts flat params to nested structures using bracket notation.
(-> handler wrap-nested-params wrap-keyword-params wrap-params)
;; {"user[name]" "Alice"} -> {:user {:name "Alice"}}
wrap-multipart-params
Handles file uploads. Adds :multipart-params with file metadata.
(wrap-multipart-params handler {:store (temp-file-store)})
;; Uploaded file structure:
;; {"file" {:filename "doc.pdf" :content-type "application/pdf"
;; :tempfile #object[java.io.File ...] :size 51200}}
Options: :encoding, :store (temp-file-store or byte-array-store). Temp files deleted after 1 hour.
Session & State
wrap-cookies
Parses cookies from headers. Adds :cookies to request, reads from :cookies in response.
(wrap-cookies handler)
;; Request: {:cookies {"session_id" {:value "abc123"}}}
;; Response: {:cookies {"session_id" {:value "abc123" :max-age 3600
;; :secure true :http-only true}}}
Attributes: :domain, :path, :secure, :http-only, :max-age, :expires, :same-site
wrap-session
Manages browser sessions via cookies. Read from :session in request, write to :session in response.
(wrap-session handler {:store (cookie-store {:key "16-byte-secret"})
:cookie-attrs {:max-age 3600 :secure true}})
;; Read: (get-in request [:session :username])
;; Update: (assoc response :session (assoc session :count 1))
;; Delete: (assoc response :session nil)
;; Recreate ID: (assoc response :session (vary-meta session assoc :recreate true))
Options: :store (memory-store default, cookie-store), :cookie-name, :cookie-attrs
Stores: memory-store (not multi-server), cookie-store (needs 16-byte key)
wrap-flash
Session-based flash messages persisting across one redirect. Must wrap around wrap-session.
(-> handler wrap-flash wrap-session)
;; Set: (assoc response :flash "Success!")
;; Read: (:flash request)
Messages auto-expire after being read once.
HTTP Protocol
wrap-head
Converts HEAD requests to GET and strips body. Usually innermost wrapper.
wrap-not-modified
Returns 304 responses via ETag/Last-Modified. Checks If-Modified-Since and If-None-Match headers.
wrap-content-type
Auto-adds Content-Type from file extension. Falls back to application/octet-stream.
(wrap-content-type handler {:mime-types {"txt" "text/plain"}})
wrap-content-length
Auto-calculates Content-Length. Uses SizableResponseBody protocol.
Static Content
wrap-file
Serves files from filesystem. Checks filesystem before handler.
(wrap-file handler "/var/www/public" {:index-files? true :allow-symlinks? false})
wrap-resource
Serves resources from classpath (JAR/WAR compatible). Path relative to resources/.
(wrap-resource handler "public") ; serves resources/public/*
Static Pattern
Combine with content-type and not-modified. These must wrap outside resource/file.
(-> handler (wrap-resource "public") wrap-content-type wrap-not-modified)
Middleware Patterns
;; Basic pattern
(defn wrap-custom [handler]
(fn [request]
(let [response (handler request)]
response)))
;; Add request keys
(defn wrap-user [handler]
(fn [request]
(if-let [user-id (-> request :session :user-id)]
(handler (assoc request :user (get-user-by-id user-id)))
(handler request))))
;; Conditional execution
(defn wrap-auth [handler]
(fn [request]
(if (authorized? request)
(handler request)
{:status 403 :body "Access Denied"})))
Common Stacks
Development Stack
(def app
(-> handler
wrap-keyword-params
wrap-nested-params
wrap-params
wrap-flash
wrap-session))
Production with Static Files
(def app
(-> handler
wrap-keyword-params
wrap-nested-params
wrap-params
wrap-flash
wrap-session
(wrap-resource "public")
wrap-content-type
wrap-not-modified))
Key Gotchas
Middleware order matters:
- wrap-params before wrap-keyword-params before wrap-nested-params
- wrap-session before wrap-flash
- wrap-resource/wrap-file outermost
- wrap-content-type and wrap-not-modified outside static middleware
wrap-multipart-params doesn't include basic params - use both wrap-params and wrap-multipart-params
Session stores:
- memory-store: not suitable for multi-server deployments
- cookie-store: requires 16-byte secret key, sessions stored in browser
wrap-file-info is DEPRECATED - use wrap-content-type + wrap-not-modified instead
Flash messages require wrap-session
Cookie security: use
:secure truefor HTTPS,:http-only trueto prevent JavaScript access,:same-site :strictfor CSRF protectionFile uploads: temp-file-store auto-deletes after 1 hour - save files permanently if needed
Session Store Protocol
Custom session stores implement SessionStore:
(require '[ring.middleware.session.store :as store])
(defrecord CustomStore []
store/SessionStore
(read-session [_ key]
(read-data-from-backend key))
(write-session [_ key data]
(let [key (or key (generate-secure-random-key))]
(save-data-to-backend key data)
key))
(delete-session [_ key]
(delete-data-from-backend key)
nil))
CRITICAL: Generate cryptographically secure random keys for new sessions to prevent session hijacking.
References
- Source: https://github.com/ring-clojure/ring/tree/master/ring-core/src/ring/middleware
- API Docs: https://cljdoc.org/d/ring/ring-core/
- Wiki: https://github.com/ring-clojure/ring/wiki
- Specific guides:
- Parameters: https://github.com/ring-clojure/ring/wiki/Parameters
- Sessions: https://github.com/ring-clojure/ring/wiki/Sessions
- Cookies: https://github.com/ring-clojure/ring/wiki/Cookies
- File Uploads: https://github.com/ring-clojure/ring/wiki/File-Uploads
- Static Resources: https://github.com/ring-clojure/ring/wiki/Static-Resources
- Middleware Patterns: https://github.com/ring-clojure/ring/wiki/Middleware-Patterns