内容提要
本文介绍 Clojure 的 Web 后端库 Ring。Ring 是对 Web 服务器的低层抽象,核心概念为 Handler、Middleware 和 Adapter,提供同步、异步和 WebSocket 三种 API。Handler 是接收请求并返回响应的函数,Middleware 用于装饰增强 Handler,Adapter 负责启动服务器。文章还介绍了路由库 Compojure 的用法,以及通过 Middleware 组合扩展功能。
延伸解读
Ring 的抽象层次与定位
Ring 是对 Web 服务器的低层抽象,不包含页面路由功能,这与 Gin、Django 等全栈框架不同。它只定义 Handler、Middleware、Adapter 三个核心概念,分别对应请求处理、功能装饰和服务器启动。这种设计让开发者能自由组合组件,但也意味着需要自行选择路由库等上层工具。理解这一定位有助于判断何时选用 Ring 以及如何搭配生态中的其他库。
Middleware 组合带来的扩展性
Middleware 接收 Handler 并返回新的 Handler,本质是装饰器模式。文章示例中,Compojure 的路由功能正是通过 wrap-response、wrap-route-matches 等 Middleware 层层包装实现的。这种机制让功能扩展不侵入业务逻辑,例如用 ring-json 自动将响应体解析为 JSON。开发者可以针对通用需求编写独立 Middleware,或直接使用现有库,从而保持代码清晰和可复用。
路由库与工具链的取舍
Compojure 是 Ring 的主要路由库,提供 routes、GET、POST 等宏,支持参数解构、动态路由和嵌套路由。文章也提到 http-kit 作为 Jetty 的替代服务器,以及 reitit 这一数据驱动的新兴路由库,但后者仍处于 0.x 版本。对于 API 文档,ring-swagger 和 compojure-api 可自动生成 Swagger,但依赖较大。作者建议根据项目复杂度权衡,简单场景手写文档可能更合适。
开发与部署的实践注意点
开发时可用 lein ring server 获得热重载,但打包时作者不建议依赖 lein ring uberjar,因为它只编译指定的 Handler,而实际应用可能还需启动后台线程或消息队列。更推荐在 core.clj 的 -main 函数中显式调用 run-jetty 或 run-server,并保留 :gen-class 以便用 java -jar 运行。这种手动启动方式让应用生命周期更可控,也便于集成其他组件。
Q&A
Ring 是什么?它和常见的 Web 框架有什么区别?
Ring 是对 Web 服务器的低层抽象,没有实现页面路由,和通常的后端框架(如 Go 的 Gin、Python 的 Django、Java 的 Spring Web 等)有很大区别。
Ring 的三个核心概念是什么?
Handler(处理器)、Middleware(中间件)和 Adapter(适配器)。
Ring 的 Handler 是什么?同步和异步 API 有什么区别?
Handler 是一个函数,接收 HTTP 请求,返回 HTTP 响应。同步 API 直接返回响应;异步 API 需要传入两个回调函数,分别用于响应和报错。一个 Handler 可以同时实现同步和异步 API。
Middleware 在 Ring 中起什么作用?
Middleware 接收一个或多个 Handler,也可能接受一些参数,最后返回新的 Handler。可以理解为装饰器模式,用来调整或增强 Handler 的行为,比如把 Handler 返回的响应体都解析成 JSON 字符串。
Adapter 在 Ring 中负责什么?
Adapter 接收一个 Handler 和其他配置参数,在调用时启动 HTTP 服务器。它把请求包装成 Request Map 传递给 Handler,随后把 Handler 返回的 Response Map 构造成 HTTP 响应发送给客户端。可以理解为代理模式。
Compojure 是什么?它如何帮助处理路由?
Compojure 是 Ring 的主要贡献者 weavejester 维护的路由库。它的核心是 routes 函数,用来把多个 Handler 组合成一个。一般和 GET、POST、PUT 等路由宏一起使用,每个宏对应一个 HTTP 方法,可以用 ANY 宏匹配任意方法。这些宏展开之后,最终返回的是对应方法和路径的 Handler。
如何用 Middleware 组合扩展 Ring 应用的功能?
可以通过 Middleware 包装 Handler 来扩展功能。例如,使用 ring-json 库的 wrap-json-body 中间件,可以把响应体 :body 里的 Clojure 数据结构自动解析为 JSON 字符串。Compojure 本身也是通过 Middleware 实现路由、请求体解构和响应体封装等功能的。