Reputation: 47609
I have some routes.
(defroutes some-routes
(GET "one" [] one)
(GET "two" [] two))
(defroutes other-routes
(GET "three" [] three)
(GET "four" [] four))
(defroutes more-routes
(GET "five" [] five)
(GET "six" [] six))
(def all-routes
(routes app-routes
(-> some-routes session/wrap-session my-interceptor)
(-> more-routes session/wrap-session my-other-interceptor)
other-routes))
I want to intercept the some-routes
but not other-routes
and perform a test based on the request (checking that a key exists in the session and some other stuff). I have more than one of these. my-other-interceptor
does the same kind of thing but different.
So I start with this:
(defn my-interceptor [handler]
(fn [request]
(prn (-> request :session :thing-key))
(let [thing (-> request :session :thing-key-id)]
(if (nil? thing)
(-> (response "Not authenticated"))
(handler request)))))
This will allow access to the handler if :thing-key
is set in the session.
Unfortunately this doesn't play nicely with having more than one set of routes. This check should only apply to some-routes
and not other-routes
. But until we execute the handler we don't know if the route matches. And at that point the handler has already executed. I could rewrite it to execute handler
and then only perform the check if the response is non-nil, but this means I've executed a handler before checking for auth.
I followed this example, which exhibits the problem:
(defn add-app-version-header [handler]
(fn [request]
(let [resp (handler request)
headers (:headers resp)]
(assoc resp :headers
(assoc headers "X-APP-INFO" "MyTerifficApp Version 0.0.1-Alpha")))))
How do I do this? What I want is:
How should I go about doing this?
Upvotes: 1
Views: 510
Reputation: 4702
The way to have separate handlers or middlewares is to decompose your routes using compojure.core/routes
and use your handler only where you need it.
In your case, if you put your other-routes
first, your problem should be solved.
As in:
(def app-routes
(compojure.core/routes
other-routes
(-> some-routes
session/wrap-session
my-interceptor)))
Remember compojure routes are just ring handlers, you can always write a custom defroutes
that calls your handler only if the route matches the request, this is the make route source code
(defn make-route
"Returns a function that will only call the handler if the method and Clout
route match the request."
[method route handler]
(if-method method
(if-route route
(fn [request]
(render (handler request) request)))))
That way, if you have more than one conditioned handler you don't need to rely on putting those routes at the end of the composition.
Notice that approach is in case you want to keep your route handling code clean.
(my-def-routes routes
(GET "/" request (show-all request))
If you don't wanna roll your own defroutes
just call your interceptor inside:
(defroutes routes
(GET "/" request (interceptor request show-all))
(defn interceptor
[request handler]
Upvotes: 2