ring
Clojure 生态最核心的 HTTP 抽象库,定义了标准的 request/response ma
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
Clojure 生态最核心的 HTTP 抽象库,定义了标准的 request/response ma
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
图1:Ring 项目标志(来源:GitHub)
如果你是一名 Clojure 开发者,想要快速搭建一个 Web 服务,你会怎么做?直接面对 Servlet API 的繁琐配置?还是引入一个重型框架,用大量注解和约定来约束你的代码?在 2010 年之前,这是 Clojure 开发者普遍面临的困境——没有一套简洁、统一的方式来处理 HTTP 请求和响应。
Ring 的出现改变了这一切。它借鉴了 Python 的 WSGI 和 Ruby 的 Rack 思想,用一个极其简洁的抽象层,将 HTTP 协议的复杂性封装起来。开发者的编程模型从此变成了一个朴素的函数签名:fn [request] → response。这个看似简单的设计,背后却支撑了 Clojure 生态中绝大多数 Web 框架的蓬勃发展。
Ring 由 Clojure 核心团队成员发起和维护,是 Clojure 生态系统中最基础的 Web 库之一。它的设计哲学是极简抽象:不捆绑任何特定的服务器实现,不强制使用某种模板引擎,也不提供 ORM。Ring 只做一件事——定义 Handler、Middleware 和 Adapter 的规范,并提供一套核心工具函数来操作请求/响应映射。
这种设计带来了一个重要后果:Ring 本身不是框架,而是框架的「地基」。基于 Ring 之上,构建了 Compojure(路由)、Liberator(REST 资源)、Luminus(应用模板)等大量知名项目。可以说,如果你不理解 Ring,就无法真正理解 Clojure Web 开发的全貌。
项目托管于 GitHub,采用 MIT 许可证,代码仓库包含多个子模块,分别负责不同功能层面的实现:
| 子模块 | 功能定位 |
|---|---|
ring/ring-core | 核心工具函数、请求/响应处理、常用 Middleware |
ring/ring-devel | 开发调试工具(如 reload、stacktrace) |
ring/ring-jetty-adapter | 内嵌 Jetty 服务器适配器 |
ring/ring-servlet | Java Servlet(≤4.0)兼容层 |
org.ring-clojure/ring-jakarta-servlet | Jakarta Servlet(≥5.0)兼容层 |
ring/ring-websocket-protocols | WebSocket 协议定义 |
想象一下,你家里的电器(电灯、冰箱、空调)只需要插上电源插座就能工作,而不需要关心发电厂用的是水电还是风电——Ring 就是 Web 开发世界里的那个「万能插座」。
这种三层分离的设计,使得 Ring 应用天然具备高度的可移植性和可组合性。
Ring 的核心是 Handler 函数,其签名如下:
(fn [request] response)
request 是一个 Clojure 映射表(map),包含了 HTTP 请求的所有信息:请求方法、URI、查询参数、请求头、请求体等。response 也是一个映射表,至少包含 :status(状态码)、:headers(响应头)和 :body(响应体)三个键:
{:status 200
:headers {"Content-Type" "text/html"}
:body "<h1>Hello, Ring!</h1>"}
这种「一切皆 map」的设计,让 HTTP 的复杂性完全消解在 Clojure 的数据字面量中,无需学习任何特殊的 DSL。
Middleware 是 Ring 最强大的特性之一。它本质上是一个高阶函数,接收一个 Handler 并返回一个新的 Handler。以下是一个带日志功能的 Middleware 示例:
(defn wrap-logging [handler]
(fn [request]
(println "收到请求:" (:uri request))
(let [response (handler request)]
(println "返回状态:" (:status response))
response)))
使用时,只需将 Middleware「套」在原始 Handler 上:
(def app (-> handler
wrap-logging
wrap-json-params
wrap-session))
使用 Ring 内置的 Jetty 适配器,一行代码即可启动服务器:
(use 'ring.adapter.jetty)
(run-jetty handler {:port 3000})
这对于本地开发、快速原型验证和小型服务来说,非常高效。
Ring SPEC 定义了完整的同步 API 和异步 API 两套规范。同步 API 简单直观,适合大多数业务场景;异步 API 则支持更高并发量的场景(如长连接、WebSocket),通过核心异步协议(ring.async.protocols)实现。
Ring 大量使用 Clojure 的 defprotocol 定义核心接口,保持了函数式编程的灵活性:
ring.core.protocols/Response — 定义响应构建接口ring.core.protocols/WrapServlet — 定义 Servlet 转换接口ring.websocket.protocols/WebSocket — 定义 WebSocket 接口ring-core 中预置了大量实用 Middleware,涵盖了 Web 开发中几乎所有通用场景:
| Middleware | 用途 |
|---|---|
wrap-params | 自动解析 URL 参数和表单体 |
wrap-keyword-params | 将参数 key 转换为 Clojure 关键字 |
wrap-session | 会话管理 |
wrap-cookies | Cookie 读写 |
wrap-file | 静态文件服务 |
wrap-content-type | 自动设置 Content-Type |
wrap-x-site | CSRF 防护 |
Ring 完全基于 JVM 运行,不依赖任何外部服务,内存占用极低(512MB 以下即可运行)。这使得它在容器化环境中非常轻量。
上手难度:非常低。 只需要安装 Leiningen 或配置 deps.edn,添加一行依赖即可开始:
;; deps.edn
{:deps {ring/ring-core {:mvn/version "1.15.4"}}}
适用场景:
不太适合的场景:
ring-devel 提供了 stacktrace 显示工具,但在生产环境需要自行配置错误处理 MiddlewareRing 是 Clojure 生态中引用量最高的项目之一,它的抽象思想深刻影响了后来函数式编程语言中 Web 框架的设计。Python 的 Starlette、Rust 的 Axum 等现代框架都能看到 WSGI/Ring 思想的影子。
截至目前,Ring 保持着活跃维护(最新版本 1.15.4),持续兼容最新的 Jakarta Servlet 规范。随着 Clojure 1.12+ 和 Java 21 的发布,Ring 也在持续吸收新的语言特性,异步支持日益成熟。
对于想要深入函数式 Web 开发的工程师而言,Ring 不是一个「轮子」,而是一扇通往更高层次抽象的大门。