Compojure also provides syntax sugar for accessing the form parameters as seen below:

(POST "/hello" [id] (str "Welcome " id))

In the guestbook application example we saw the following route defined:

(POST "/" [name message] (save-message name message))

Note that POST requests must contain a CSRF token by default. Please refer here for more details on managing CSRF middleware.

This route extracts the name and the message form parameters and binds them to variables of the same name. We can now use them as any other declared variable.

It's also possible to use the regular Clojure destructuring inside the route.

(GET "/:foo" {{foo "foo"} :params}
(str "Foo = " foo))

Furthermore, Compojure allows destructuring a subset of form parameters and creating a map from the rest.

[x y & z]
x -> "foo"
y -> "bar"
z -> {:v "baz", :w "qux"}

Above, parameters x and y have been bound to variables, while parameters v and w remain in a map called z. Finally, if we need to get at the complete request along with the parameters we can do the following:

(GET "/" [x y :as r] (str x y r))

Here we bind the form parameters x an y, and bind the complete request map to the variable r.

Return values

The return value of a route block determines at least the response body passed on to the HTTP client, or at least the next middleware in the ring stack. Most commonly, this is a string, as in the above examples. But, we may also return a response map:

Th :file request form parameter points to a map containing the description of the file that will be uploaded. Our upload-file funciton above uses :tempfile, :size and :filename keys from this map to save the file on disk.

If you're fronting with Nginx then you can easily support file upload progress using its Upload Progress Module.

Organizing application routes

It's a good practice to organize your application routes together by functionality. Compojure provides the defroutes macro which can group several routes together and bind them to a symbol.

Once all your application routes are defined you can add them to the main handler of your application. You'll notice that the template already defined the app in the handler namespace of your application. All you have to do is add your new routes there.

Note that you can also apply custom middleware to the routes using wrap-routes as seen with home-routes. The middleware will be resolved after the routes are matched and only affect the specified routes as opposed to global middleware that's referenced in the middleware/wrap-base.

Restricting access

Some pages should only be accessible if specific conditions are met. For example, you may wish to define admin pages only visible to the administrator, or a user profile page which is only visible if there is a user in the session.

Restricting access based on route groups

The simplest way to restrict access is by applying the restrict middleware to groups of routes that should not be publicly accessible. First, we'll add the following code in the <app>.middleware namespace:

We'll wrap the authentication middleware that will set the :identity key in the request if it's present in the session. The session backend is the simplest one available, however Buddy provides a number of different authentications backends as described here.

The authenticated? helper is used to check the :identity key in the request and pass it to the handler when its present. Otherwise, the on-error function will be called.

This is the default authentication setup that will be produced using the +auth profile when creating a new project.

We can now wrap the route groups we wish to be private using the wrap-restricted middleware in the <app>.handler/app function:

Next, we'll create the access rules for our routes. The rules are defined using a vector where each rule represented using a map. A simple rule that checks whether the user has been authenticated can be seen below.

(def rules
[{:uri "/restricted"
:handler authenticated?}])

We'll also define an error handler function that will be used when access to a particular route is denied: