Ring is an easy-to-use library for writing web applications in Clojure, inspired by Python's Web Server Gateway Interface (WSGI) and Ruby's Rack. Ring enables web applications to be built of modular components that can be shared among various applications, web servers, and web frameworks by abstracting the specifics of HTTP into a straightforward, unified application programming interface (API).
Here is the complete request/response process in Ring as shown in the diagram.
The client requests a webpage, and a query is sent to the server.
The HTTP request moves toward Ring before actually moving toward the server.
Ring checks and modifies the request (converts it into a Clojure hash map) and then passes it to the middleware.
Middleware sends the request to the handler.
The handler checks the request map, performs all the necessary functions, and returns the response map with the desired data.
The response, as a map, moves from the handler to the middleware and Ring.
Ring transforms the response map to the HTTP response and sends it back to the user.
In the context of Ring, the server refers to the component that coordinates handling incoming HTTP requests, applying middleware transformations, and dispatching the requests to appropriate handler functions.
Here's a brief explanation of the main components of a Ring server.
Ring framework works as a middleman between the client and the server, parsing the incoming HTTP request into Clojure hash maps. It coordinates the flow of requests through the middleware stack and eventually to the designated handler function. The server is often started on a specific port and host, allowing it to listen for incoming connections. The app handler also transforms the response from the server into a Clojure hash map. The transformation of requests and responses into hash maps enables us to work with HTTP messages efficiently and seamlessly. Hash maps use the technique of hashing, which converts an object into an integer value. The integer value helps in indexing and faster searches.
Handler is the function that takes the request map and returns the response with its status and body.
Here are some examples of handler functions:
(defn hello-world-handler[_]{:status 200:headers {}:body "Hello World"})
Lines 1–5 (hello-world-handler
): A function, hello-world-handler
, which takes an unused argument, is defined. It returns a response map with the status 200
, empty headers, and "Hello World"
as the body.
(defn bad-request-handler[_]{:status 400:headers {}:body "Bad Request: The request could not be understood by the server."})
Lines 1–5 (bad-request-handler
): These lines define a function bad-request-handler
that takes an unused argument and returns a response map indicating the status 400
(Bad Request
), with empty headers, and a body message explaining the issue.
Just like the infamous Error 404: Not found
, all the requests with status codes of 400
and onward are considered bad requests that can’t be returned with the data.
(defn internal-server-error-handler[_]{:status 500:headers {}:body "Internal Server Error: The server encountered an unexpected condition."})
Lines 1–5 (internal-server-error-handler
): A function called internal-server-error-handler
is defined, which takes an unused argument. It returns a response map containing the status of 500
(Internal Server Error
), empty headers, and a body message describing an unexpected server condition.
Sometimes, the server gets down due to unexpected behavior. In that case, the handler will respond with the status code 500
.
Middleware is also a function between the server and the handler that can modify the request (transform it into the Clojure hash map ) before it reaches the server. This modification allows the communication to be faster and more efficient. In the same way, middleware also modifies the response it receives from the server before it’s passed on to the client through the app.
Here is a middleware function in Clojure:
(defn middleware-add-header[handler-fn](fn [req](let [response (handler-fn req)](assoc-in response [:headers "X-Custom-Header"] "Middleware Example"))))(defn main-handler[req]{:status 200:headers {}:body "Hello from the main handler!"})(defn combined-handler [req](-> main-handler(middleware-add-header)));; Usage(def req {:method :get:uri "/example"})(def response (combined-handler req))
Lines 1–2 (middleware-add-header
): These lines define a function middleware-add-header
that takes handler-fn
as an argument and returns a new function. The function returned takes req
as an argument.
Lines 3–5 (middleware-add-header
): The middleware-add-header
function invokes handler-fn
with the provided req
to get a response
. It uses the assoc-in
function to add a custom header X-Custom-Header
with the value Middleware Example
to the response
. Then, it returns the modified response
with the added header.
Lines 7–11 (main-handler
): The function main-handler
is defined that takes req
as an argument and returns a map representing a response with the following keys:
:status
: Set to 200
(OK status code).
:headers
: An empty map indicating no additional headers initially.
:body
: Contains the string, “Hello from the main handler!”
.
Lines 13–15 (combined-handler
): They define a function combined-handler
using the threading macro (->
) that threads the result of main-handler
through the middleware-add-header
function. It composes the middleware and main handler to create a modified handler.
Lines 18–21 (Usage
): In this part, a sample request map named req
is defined. Finally, combined-handler
with the req
to get a modified response is invoked. The response is stored in the response
variable. The sample request map has the following keys:
:method
: Set to :get
, indicating a GET request.
:uri
: Set to "/example"
, representing the requested Uniform Resource Identifier (URI).
This structure allows the easy creation of a series of middleware functions that can process requests and responses as they pass through the pipeline, demonstrating the ability to manipulate the data between the main handler and the final response.
Free Resources