-
Notifications
You must be signed in to change notification settings - Fork 6
Concepts
A web application developed for Ring consists of four components:
- Handler
- Request
- Response
- Middleware
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-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.
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 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.