As you’ve seen throughout this book, Clojure excels at data transformation. However, we are actually most fortunate when there is no need for a transformation to occur: that is, when a suitable format exists for your domain, don’t go out of your way to invent a new one. In that spirit, and with some inspiration from Python’s WSGI and Ruby’s Rack, Ring’s SPEC defines a standard data schema to represent web requests and responses using Clojure data structures, and a couple of key architectural concepts based on function composition: adapters, handlers, and middleware.
Understanding the Ring SPEC is crucial to being able to effectively build web applications in Clojure. We’ll explore each of its aspects here, including some parts of the SPEC verbatim as appropriate. We encourage you to read the Ring SPEC in its entirety at least once, and keep it close at hand as you learn to work with the data and abstractions it defines.
Whereas many other frameworks define fixed APIs for accessing web request data—like the requested URI, request headers, query and post parameters, body content, and so on—and still other APIs for sending web responses, Ring represents both requests and responses as regular Clojure maps. In both cases, these maps must contain certain slots, may contain others, and can be used to hold any other data you require in the course of processing them.
Ring request maps (Table 16-1) contain these keys (optional slots ...