Skip to content
weavejester edited this page Aug 7, 2011 · 2 revisions

A web application developed for Ring consists of four components:

  • Handler
  • Request
  • Response
  • Middleware

Handlers

Handlers are functions that define your web application. They take one argument, a map representing a HTTP request, and return a map representing the HTTP response.

Let's take a look at an example:

(defn what-is-my-ip [request]
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body (:remote-addr request)})

This function returns a map that Ring can translate into a HTTP response. The response returns a plain text file that contains the IP address that was used to access the web application.

The handler function can then be converted into a web application through a variety of different methods which will be covered in the next section.

Requests

As previously mentioned, HTTP requests are represented by Clojure maps. There are a number of standard keys that will always exist, but requests can (and often) contain custom keys added by middleware.

The standard keys are:

  • :server-port The port on which the request is being handled.

  • :server-name The resolved server name, or the server IP address.

  • :remote-addr The IP address of the client or the last proxy that sent the request.

  • :uri The request URI (the full path after the domain name).

  • :query-string The query string, if present.

  • :scheme The transport protocol, either :http or :https.

  • :request-method The HTTP request method, which is one of :get, :head, :options, :put, :post, or :delete.

  • :content-type The MIME type of the request body, if known.

  • :content-length The number of bytes in the request body, if known.

  • :character-encoding The name of the character encoding used in the request body, if known.

  • :headers A Clojure map of lowercase header name strings to corresponding header value strings.

  • :body An InputStream for the request body, if present.

Responses

The response map is created by the handler, and contains three keys:

  • :status The HTTP status code, such as 200, 302, 404 etc.

  • :headers A Clojure map of HTTP header names to header values. These values may either be strings, in which case one name/value header will be sent in the HTTP response, or a collection of strings, in which case a name/value header will be sent for each value.

  • :body A representation of the response body, if a response body is appropriate for the response's status code. The body can be one of four types:

    • String The body is sent directly to the client.

    • ISeq Each element of the seq is sent to the client as a string.

    • File The contents of the referenced file is sent to the client.

    • InputStream The contents of the stream is sent to the file. When the stream is exhausted, the stream is closed.

Middleware

Middleware are higher-level functions that add additional functionality to handlers. The first argument of a middleware function should be a handler, and its return value should be a new handler function.

Here is a simple example:

(defn wrap-content-type [handler content-type]
  (fn [request]
    (let [response (handler request)]
      (assoc-in response [:headers "Content-Type"] content-type))))

This middleware function adds a "Content-Type" header to every response generated by the handler.

To apply this middleware to a handler:

(def app
  (wrap-content-type handler "text/html"))

This defines a new handler, app that consists of the handler handler with the wrap-content-type middleware applied.

The threading macro (->) can be used to chain middleware together:

(def app
  (-> handler
      (wrap-content-type "text/html")
      (wrap-params)
      (wrap-keyword-params)))

Middleware is used often in Ring, and is used to provide much of its functionality beyond handling raw HTTP requests. Parameters, sessions, and file uploading is all handled by middleware in the Ring standard library.