Created project using lein new compojure project-name
and have the server referencing (wrap-defaults project-name site-defaults)
.
Inside my handler I am calling controllers and passing the params to those controllers. Project structure:
handler.clj ;; ring accesses def app
controllers/app.clj ;; where route params are sent
example: (GET "/signup" {params :params} (controller-app/signup params))
So the issue I am having is I cannot figure out how to get or set cookies from the controllers file. Things I have tried:
Any help would be much appreciated. There is not much documentation on this so unfortunately the problem has taken a fair amount of time.
Finally was able to solve it by brute force. It's no joke when people say the documentation for Clojure is pretty sparse. A couple of notes on ring:
Sessions are not what you think of when you hear sessions. It's just a signed cookie and a poorly documented one at that. Just ignore it. The few tutorials I did find constantly misspoke and used "sessions" when they meant "signed cookie". The documentation even uses cookies and sessions interchangeably. Why? I have no idea as they are completely separate methods of storing data. Sessions are a server side in memory store and cookies are a client side browser store.
:headers are needed otherwise the cookie will just download into an empty text file. Took forever to find out why this was the case.
Now onto how to do this:
Here you pass the cookie to the controller from inside your handler. Cookies are available by default if you used "lein new compojure app-name" to create your app. I had to read the source code to figure this out.
default namespace (app-name/handler.clj) -
(ns some-namespace.handler
[compojure.core :refer :all]
[compojure.route :as route]
[ring.middleware.defaults :refer [wrap-defaults site-defaults]
[app-name.controllers.home :as home-controller))
Your app routes (app-name/handler.clj) -
(defroutes app-routes
(GET "/" {cookies :cookie} (home-controller/home cookies)))
Here is how you set the cookie itself (app-name/controllers/home.clj).
(ns app-name.controllers.home)
(defn home [cookies]
{:headers {"Content-Type" "Set-Cookie"},
:cookies {"cookie-name" {:value "cookie-value", :path "/"}},
:body "setting a cookie"})
Bottom of handler.clj using default wraps for specified routes (app-name/handler.clj)
(def app
(wrap-defaults app-routes site-defaults ))
This was a very simple problem that turned out to be far more complex. It took me 3 days to figure all of the above out. I am new to Clojure/Ring/Compojure but this has been the worst experience I have had yet programming.
It's really a problem of just enough abstraction to become dangerous (so basically nothing is obvious). Libraries like Ring REALLY need to be better documented and over explained if wider adoption is wanted.
Anyways, I hope this helps someone.
I personally spent quite some time to making cookie reading work due to my poor understanding of destructuring in compojure.
After wrapping yout application with the wrap-cookies handler, you need to destructure cookies variable "the clojure way" and not "the compojure way" since the cookies map is not in the request params map.
Example that doesn't work :
(GET "/read-cookie" [cookies]
(let [name (get-in cookies ["name" :value] "")]
(if (empty? name)
(layout/common [:h1 "I don't read a cookie"])
(layout/common [:h1 (str "I read a cookie : " name)]))))
Example that works :
(GET "/read-cookie" {:keys [cookies]} ;; or {cookies :cookies}
(let [name (get-in cookies ["name" :value] "")]
(if (empty? name)
(layout/common [:h1 "I don't read a cookie"])
(layout/common [:h1 (str "I read a cookie : " name)]))))
Hope this helps,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With