Skip to content

How to use a controller interceptor

macourtney edited this page Sep 13, 2010 · 7 revisions

Added for version: 0.6
Updated for version: 0.7

A controller interceptor can be used to intercept an incoming request to reroute, alter the request-map, or alter the response. An interceptor is simply a function which takes both the incoming request-map and the action which would be called if the request was not intercepted. The interceptor must return a response which may reroute the request to another action, or return a response.

Here is an example interceptor which simply logs “Hello World!” for every request:

(defn hello-interceptor [action]
  (logging/debug "Hello World!")
  (action))

Create a new conjure app called “hello_world” and add the above interceptor to the home_controller.clj file. Don’t forget to require clojure.contrib.logging in hello-controller.

Adding an Interceptor

Now lets make the interceptor intercept all actions in home-controller. To add an interceptor to a controller use the add-interceptor macro in conjure.core.controller.base. Add-interceptor can take either just the interceptor or the interceptor and parameters. Lets add the interceptor without any parameters first.

Add the following code to home-controller:

(add-interceptor hello-interceptor)

The above code adds the hello-interceptor to all actions in the hello-controller. If you restart your server and point your browser at the home page, “Hello World!” will be printed to your console.

Adding an Interceptor With Excludes

If you try to go to /home/list_records, “Hello World!” will actually be displayed twice, once for the call to list-records and a second time for index which list-records redirected to. The same thing happens if you try to go to /home/add as well.

We could keep “Hello World!” from displaying twice if we simply exclude the list-records and add actions. To exclude list-records and add we need to pass parameters along with the interceptor. Params is a map may contain the keys :excludes, :includes and :interceptor-name. We’ll set :excludes to the set #{ :list-records, :add }.

Our new call to add-interceptor looks like:

(add-interceptor hello-interceptor { :excludes #{ :list-records, :add } })

When you go to /home/list_records only one “Hello World!” is displayed.

Adding an Interceptor With Includes

Unfortunately, we still have a problem with our error-404 action. Every time a 404 error occurs, “Hello World!” will be printed. We could add error-404 to the list of actions to exclude, but this doesn’t make much sense. We’re excluding nearly every action in the home controller. If we add new actions we’ll have to add them to the excludes list if we don’t want “Hello World!” to be displayed.

If we only want “Hello World!” to display for certain actions, but not all, we can set :includes instead of :excludes in the add-interceptor parameters. Includes is also a set listing all of the actions to intercept. Lets update our add-interceptor call to intercept only the index action.

Our new add-interceptor call looks like:

(add-interceptor hello-interceptor { :includes #{ :index } })

Now, if we cause a 404 error “Hello World!” is not logged. Also, if we go to list-records or add, “Hello World!” is logged only once when the index action is displayed.

What if you pass both :includes and :excludes to add-interceptor? If both :includes and :excludes are passed to add-interceptor, then only :includes is used (:excludes is ignored completely).

Thus, the following does not change how our app works:

(add-interceptor hello-interceptor { :includes #{ :index }, :excludes #{ :index } })

Adding an App Interceptor

What if we want our interceptor to be called for every action in all controllers? We can add an interceptor to all actions in all controllers by using the add-app-interceptor function.

You could add an app interceptor in a controller, but there’s no guarantee the controller will be loaded by Conjure for every action. So, Conjure includes an app.clj file in your controllers directory. App.clj will be loaded when the server starts. You should add your app interceptors in the app.clj file.

First, move the hello-interceptor function into app.clj. Don’t forget to require clojure.contrib.logging as logging in the app.clj file.

Next, simply call add-app-interceptor and pass it hello-interceptor. The call to add-app-interceptor looks like:

(add-app-interceptor hello-interceptor)

After you restart the server, “Hello World!” will be logged for every action.

Now, we’re back to our old problem of displaying “Hello World!” twice for list-records and add. To handle this issue, add-app-interceptor allows you to pass an excludes map. To exclude list-records and add in the home controller, use the following:

(add-app-interceptor hello-interceptor { :excludes { :home #{ :list-records, :add } } })

Notice, :excludes is now a map from controller name to a set of actions. Since app interceptors are for the entire app, we have must tell it what controller the actions are in.

When you restart your server, list-records and add will not log “Hello World!” twice anymore.

What about :includes? Actually, includes does not make sense at the app level. Since an app interceptor is supposed to cover the entire app, only including certain controllers and actions defeats the purpose of an app interceptor. If you only want to include only certain actions in certain controllers, then create a controller interceptor and use includes.

Clone this wiki locally