So I’ve been working with Clojure webapps again lately and I noticed how I keep forgetting what middleware is, what it does, and how to use it. This is a shame because it’s actually not very complicated and it’s quite useful once you get it. So I’m going to place here a write-up on middleware and an example of its use.
Anyway, middleware is largely a part of the Ring library which is the foundation of almost every Clojure webapp or webapp frameworks available. Ring acts as the interface between various web servers which Clojure can use (mainly Java servlets) and higher level frameworks and libraries such as Noir or Moustache. Ring deals with the work of converting http requests into a form which Clojure programs can use, handing them off to a handler function in the Clojure webapp, converting the return value of the handler to a proper http response, and passing that response to the server so that it can be returned over the Internet. What happens in the handler depends entirely on either on your specific framework or your application if you don’t use a framework.1
There are three things to be worried about here:
- The Request, which is a Clojure data structure which represents the actual request received from the Internet.
- The Response, which is a Clojure data structure which represents the actual response that is going to be sent to the Internet.
- The Handler, which is a Clojure function which receives one and returns the other of the previous two things.
This is all well and good but what does it have to do with middleware? Well, sometimes you want to modify the way requests or responses are dealt with,2 without mucking with the internals of the handler. This might be because the of way your framework rewrites or dispatches URIs or it might just be the simplest place to insert some functionality. Either way, middleware provides a simple way to do this by simply wrapping the handler. A middleware function is a function which receives a handler function as its first argument and any number of further arguments, and returns a different handler function as its return value. So for a trivial example:
(defn wrap-example-middleware [handler] (fn [request] (handler request)))
This might be applied by wrapping a handler function before passing it to ring:
(defservice (wrap-example-middleware handler))
Now that was a trivial example and literally did nothing so I’ll show you an example I created today.
One piece of functionality I often find conspicuously missing in webapp frameworks is the ability to place the entire web application under a URI prefix. So for example, if I wanted to host several webapps under the same domain name I could place them under “http://example.com/app1”, “http://example.com/app2”, etc, without embedding the prefix into the application’s own routing logic. Seeing as I only have one server and a whole bunch of projects I’m working on at any one moment, this is a very useful feature for me. So fortunately, while the web framework Noir, which I’m using at the moment doesn’t support this, middleware makes it simple to add:
(defn wrap-uri-prefix [handler prefix] (fn [request] (handler (assoc request :uri (string/replace-first (:uri request) (re-pattern (str "^" prefix "/?")) "/")))))
This function receives a handler function and a prefix string, and returns a handler which removes the prefix from incoming URIs before doing anything else. This way the application can dispatch on “/home” or “/users” and ignore the “/application-foo” prefix on all the requests it’s receiving. Notice that a request object is simply a Clojure map with entries corresponding to http request fields. The :uri field is replaced before the request is passed to the handler.
Now, this isn’t quite complete because even though we’ve dealt with incoming requests, we haven’t yet dealt with links in the application’s html or with redirects, both of which need the prefix to be added to them for the application to work correctly. Now, links are tricky here because they don’t actually exist in the request or response objects. They exist in the html in the response object, but parsing and editing the html in each response is the wrong the way to accomplish this. Fortunately for me, I’m using the Hiccup html generation library provides a “with-base-url” macro which adds a prefix to all links generated by the library within its scope, so I can easily avoid addressing this particular problem.
I still need to work a little for redirects though. To deal with these, I first look only for redirects with a response status of 30* (redirect) and then replace the “Location” header, which contains the redirect URI, with a URI with the prefix added. The final code looks like this:
(ns application.uri-prefix (:require [noir.server :as server] [hiccup.core :as hiccup])) (defn wrap-uri-prefix [handler prefix] (fn [request] ; start defining function to return (hiccup/with-base-url prefix ; set prefix for generated links (let [response (handler (assoc request :uri (string/replace-first (:uri request) (re-pattern (str "^" prefix "/?")) "/")))] (if (<= 300 (:status response) 308) ; Only rewrite redirects (assoc response :headers (assoc (:headers response) "Location" (string/replace-first (get-in response [:headers "Location"]) #"^/" (str prefix "/")))) response))))) (server/add-middleware wrap-uri-prefix prefix) ;noir specific function to add middleware
So there you have it. Middleware and how to use it.
- Generally there is a dispatching function which looks at the incoming URI and chooses another sub-handler to based on it. So, “http://example.com/home” would be handled by one function and “http://example.com/users” would be handled by another. ↩
- Such as causing the app to log each request or rewrite URIs. ↩