Compojure/Core Libraries
This page may need to be updated to reflect current knowledge. You can help update it, discuss progress, or request assistance. |
Important note about up-to-date information
[edit | edit source]See the official Compojure wiki for up-to-date information. This page is severely outdated and should not be relied upon as a guide for recent versions.
compojure.http.*
[edit | edit source]The HTTP library provides Compojure with a RESTful and functional way to define Java servlets. Its syntax was inspired by the Ruby web framework, Sinatra.
To create a servlet, you pass a series of HTTP resource definitions to the servlet function:
(def #^{:doc "A simple greeter"} greet (servlet (GET "/greet" "Hello visitor!") (ANY "/*" (page-not-found))))
Compojure also provides a defservlet macro:
(defservlet greet "A simple greeter" (GET "/greet" "Hello visitor!") (ANY "/*" (page-not-found)))
The resource definitions passed to defservlet are Compojure’s way of associating a URL route with code that produces a useful HTTP response, such as a web page or image.
Resource Definitions
[edit | edit source]Resource definitions take the form:
(method route & body)
The method can be any one of the standard HTTP methods:
GET POST PUT DELETE HEAD
Or, if you wish to match any HTTP method, you can use
ANY
Faking PUT and DELETE for HTML Forms
[edit | edit source]By current standards HTML forms can only call the GET and POST methods, but in a RESTful web service you may want to implement PUT and DELETE methods for your resources. Compojure provides a work-around that allows you to implement REST's uniform interface once for browsers and less restricted clients with no explicit server-side handling. The client should send a parameter named "_method" with the value of the method you really want called. You can add a hidden field to HTML forms for this purpose, and other clients can simply call PUT or DELETE without the parameter. Compojure's handler infrastructure will replace the actual method with the "_method" parameter's value before calling your handlers.
See the section #Servlet Bindings for an example that uses this technique.
Route Parameters
[edit | edit source]The route can be a fixed string, like “/greet”, but often you’re going to want to assign certain parts of the route to parameters that affect the output:
(GET "/greet/:name" {params :params} (str "Hello " (params :name)))
Here, the resource definition assigns the path after “/greet” to the parameter :name. Parameters from routes can be accessed via the route function.
Servlet Bindings
[edit | edit source]Along with route, there are several other bindings available by default in all resource declarations:
- method - the HTTP method
- params - a hash-map of HTTP parameters
- headers - a hash-map of HTTP headers
- cookies - a hash-map of HTTP cookies
- session - a ref to a session-specific map
- request - the HttpServletRequest object
Here's an example that uses the session and params bindings:
(GET "/name" (str "<p>Your current name is: " (@session :name) "</p>" "<form>Change name: <input name='name' type='text'>" "<input type='submit' value='Save'></form>"))
(POST "/name" (dosync (alter session assoc :name (params :name)) (str "Your name was changed to " (@session :name))))
Here's the same example but with the update implemented as PUT with the "_method" work-around:
(GET "/name" (str "<p>Your current name is: " (@session :name) "</p>" "<form>Change name: <input name='name' type='text'>" "<input type='submit' value='Save'>" "<input type='hidden' name='_method' value='PUT'></form>"))
(PUT "/name" (dosync (alter session assoc :name (params :name)) (str "Your name was changed to " (@session :name))))
Generating the Response
[edit | edit source]It is possible to modify the response through the response object, but this is almost never necessary. Instead, Compojure takes a functional approach, constructing the HTTP response from the return value of the resource.
In the previous examples, you can see how returning a string adds to the response body. Other standard Clojure types modify the response in different ways:
- java.lang.String - adds to the response body
- java.lang.Number - changes the status code
- Clojure map - updates (merges) the response Map
- Clojure seq - lazily adds to the response body
- java.io.File - streams the file to the response body
- java.io.InputStream - reads from the stream and adds to the response body
- java.net.URL - streams the resource of the URL to the response body
- java.servlet.http.Cookie - adds the cookie to the HTTP headers
These modifications can be chained together using a standard Clojure vector:
(GET "/text" [{:headers {"Content-Type" "text/plain"}} "This is plain text." "And some more text."])
(GET "/bad" [404 "<h1>This page does not exist!</h1>"])
(GET "/download" (file "public/compojure.tar.gz")) ; 'file' is an alias to 'new java.io.File'
compojure.html
[edit | edit source]The HTML library provides a way of defining HTML or XML through a tree of vectors.
(html [:p [:em "Hello World"]])
<p><em>Hello World</em></p>
The tag name is taken from the first item of the vector, and can be a string, symbol or keyword. You can optionally specify attributes for the tag by providing a hash map as the second item of the vector:
(html [:div {:class "footer"} "Page 1"])
<div class="footer">Page 1</div>
The html function additionally offers syntax sugar for defining id and class attributes:
(html [:h1.first "Foo"] [:h2#second "Bar"])
<h1 class="first">Foo</h1><h2 id="second">Bar</h2>
Any sequences will be expanded out into the containing vector:
(html [:em '("foo" "bar")])
<em>foobar</em>
(html [:ul (map (fn [x] [:li x]) [1 2 3])])
<ul><li>1</li><li>2</li><li>3</li></ul>
compojure.server.jetty
[edit | edit source]The Jetty library provides a Clojure-friendly interface to the Jetty web server, so that you can easily create a web server with servlet mappings.
(def my-server (http-server {:port 8080} "/*" my-main-servlet "/other/*" another-servlet ...))
You can also use the defserver macro:
(defserver my-server {:port 8080} "/*" my-main-servlet "/other/*" another-servlet ...))
Once you’ve created your Jetty server, use (start my-server) and (stop my-server) to start and stop the web server.