-
Notifications
You must be signed in to change notification settings - Fork 526
Concepts
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.
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-portThe port on which the request is being handled. -
:server-nameThe resolved server name, or the server IP address. -
:remote-addrThe IP address of the client or the last proxy that sent the request. -
:uriThe request URI (the full path after the domain name). -
:query-stringThe query string, if present. -
:schemeThe transport protocol, either:httpor:https. -
:request-methodThe HTTP request method, which is one of:get,:head,:options,:put,:post, or:delete. -
:content-typeThe MIME type of the request body, if known. -
:content-lengthThe number of bytes in the request body, if known. -
:character-encodingThe name of the character encoding used in the request body, if known. -
:headersA Clojure map of lowercase header name strings to corresponding header value strings. -
:bodyAn InputStream for the request body, if present.
The response map is created by the handler, and contains three keys:
-
:statusThe HTTP status code, such as 200, 302, 404 etc. -
:headersA 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. -
:bodyA 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:-
StringThe body is sent directly to the client. -
ISeqEach element of the seq is sent to the client as a string. -
FileThe contents of the referenced file is sent to the client. -
InputStreamThe contents of the stream is sent to the file. When the stream is exhausted, the stream is closed.
-
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.