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
editSee 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.*
editThe 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
editResource 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
editBy 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
editThe 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
editAlong 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
editIt 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
editThe 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
editThe 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.