Back to skills
SKILL.md
Clojure Ring Core Middleware
ASecurityRing 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.
- 666 stars
- 0 votes
- 0 copies
- 1 view
- Added February 8, 2026
Works with
Security analysis
100/100npx -y skills add majiayu000/claude-skill-registry --skill clojure-ring-core-middleware --agent claude-codeAre you the author of Clojure Ring Core Middleware?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/majiayu000-clojure-ring-core-middleware)---
name: clojure-ring-core-middleware
description: |
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.
---
# 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:
```clojure
(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.
```clojure
(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.
```clojure
(-> handler wrap-keyword-params wrap-params)
;; {"q" "clojure"} -> {:q "clojure"}
```
## wrap-nested-params
Converts flat params to nested structures using bracket notation.
```clojure
(-> 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.
```clojure
(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.
```clojure
(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.
```clojure
(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.
```clojure
(-> 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`.
```clojure
(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.
```clojure
(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/`.
```clojure
(wrap-resource handler "public") ; serves resources/public/*
```
## Static Pattern
Combine with content-type and not-modified. These must wrap outside resource/file.
```clojure
(-> handler (wrap-resource "public") wrap-content-type wrap-not-modified)
```
# Middleware Patterns
```clojure
;; 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
```clojure
(def app
(-> handler
wrap-keyword-params
wrap-nested-params
wrap-params
wrap-flash
wrap-session))
```
## Production with Static Files
```clojure
(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
1. 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
2. wrap-multipart-params doesn't include basic params - use both wrap-params and wrap-multipart-params
3. Session stores:
- memory-store: not suitable for multi-server deployments
- cookie-store: requires 16-byte secret key, sessions stored in browser
4. wrap-file-info is DEPRECATED - use wrap-content-type + wrap-not-modified instead
5. Flash messages require wrap-session
6. Cookie security: use `:secure true` for HTTPS, `:http-only true` to prevent JavaScript access, `:same-site :strict` for CSRF protection
7. File uploads: temp-file-store auto-deletes after 1 hour - save files permanently if needed
## Session Store Protocol
Custom session stores implement SessionStore:
```clojure
(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
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.