4
votes

Usually Ring middleware is associated with the use on the server side. In the post I'll discuss how the concept of ring middleware can be applied to http clients.

A very typical server side example might look like this:

(def server
  (-> server-handler
      wrap-transit-params
      wrap-transit-response))

Desugared:

(def server (wrap-transit-response (wrap-transit-params handler)))

server is a function now, which accepts a request hash-map. Middleware can operate on this data before its send to the handler. It can also operate on the response hash-map that the handler returns. Or on both. It can even manipulate the execution of the handler.

Server

The above middleware could look like this in a very simplified way:

(1.) This operates on data before it gets to the actual handler (request, incoming data), parsing the body and providing the result as value to the :params key. It's called pre-wrap.

(defn wrap-transit-params [handler]
  (fn [req]
    (handler (assoc req :params (from-transit-str (req :body))))))

(2.) This one manipulates the outgoing data, the response of the server, outgoing data. - It's a post-wrap.

(defn wrap-tranist-response [handler]
  (fn [req]
    (let [resp (handler req)]
      (update resp :body to-transit-str))))

With this a server can receive and respond data as transit.

Client

The same behavior could be desirable for an http-client.

(def client
  (-> client-handler
      wrap-transit-params
      wrap-transit-response))

It turns out that the above middleware cannot easily be reused as client middleware, even though there is some symmetry.

For the client-side they should be implemented like this:

(defn wrap-transit-params [handler]
  (fn [req]
    (handler (assoc req :body (to-transit-str (req :params))))))

(defn wrap-transit-response [handler]
  (fn [req]
    (let [resp (handler req)]
       (update resp :body from-transit-str)))) 

Now it could be used like this:

(client {:url "http://..."
         :params {:one #{1 2 3}}})

Since in reality there would be much more things invloved, so I think having reusable middleware for both server and client side is utopian.

Though it remains up to discussion if the concept generally makes sense for clients. I could not find any client side middleware on the net.

The server side middleware is usually under the namespace ring.middleware... My concrete question here is if a library providing client side middleware should use this namespace or not.

1

1 Answers

0
votes

Unless you believe that code that uses your new library could be written to be completely portable between client and server, I feel that using a different namespace will lead to less confusion. Find a fun name! :)