I'm two days into learning Clojure by writing a simple REST server using ring-clojure and Compojure with ring-json's wrap-json-body
middleware.
So far, I have:
A vector users
containing the users (with a couple of default users):
(def users [{:id 0 :username "aname"}
{:id 1 :username "anothername"}])
A function (form?) save-user
that accepts a map (user) and looks for existing users with the same username. If the username is available, I overwrite the users
vector to include the new user before returning HTTP 201. If the username is taken, I simply return HTTP 400:
(defn save-user [user]
(prn users)
(if
(not-any? #(= (:username %) (:username user)) users)
(fn [request]
(def users (conj users user))
(status
(response (str "Saved user with username: " (:username user)))
201))
(status
(response (str "User with username '" (:username user) "' already exists"))
400)))
A route for POST /users
which calls save-user
with the received map:
(defroutes app-routes
(POST "/users" request (save-user (:body request))))
I don't think it matters, but the middleware is applied like this:
(def app
(-> app-routes
(wrap-cors :access-control-allow-origin "*" :access-control-allow-methods "*")
(wrap-json-response)
(wrap-keyword-params)
(wrap-json-body {:keywords? true :bigdecimals? true})
(wrap-defaults (assoc site-defaults :security false))))
My problem:
For whatever reason, the entire request
map is passed to the function i pass as then
inside the if
. Printing it:
(if
(not-any? #(= (:username %) (:username user)) users)
(fn [request]
(prn request))
...
... gives me this:
{:ssl-client-cert nil, :cookies {}, :remote-addr "0:0:0:0:0:0:0:1", :params {}, :flash nil, :route-params {}, :headers {"host" "localhost:3000", "accept" "*/*", "content-length" "42", "content-type" "application/json", "user-agent" "curl/7.43.0"}, :server-port 3000, :content-length 42, :form-params {}, :compojure/route [:post "/users"], :session/key nil, :query-params {}, :content-type "application/json", :character-encoding nil, :uri "/users", :server-name "localhost", :query-string nil, :body {:username "testusername", :password "testpassword"}, :multipart-params {}, :scheme :http, :request-method :post, :session {}}
The same happens if I pass an anonymous function as the if's else
. However, nothing wrong happens when I only pass (status ...)
, like in the code above.
From what I understand, the request
map shouldn't be available inside save-user
at all, since it's not passed as an argument. Why is it passed to my anonymous function, and is there any way to simply ignore it?
save-user
function at all? You could just return the response directly (replace(fn [request]
with(do
). – gltsusers
vector in anatom
and then operate on the atom. Redefining global vars from inside a function is a big no-no in Clojure. – gltssave-user
, but I'm passing one to theif
because I need to both save the user and return the HTTP response object (returned from thestatus
function). As for wrapping the list in anatom
: thanks! That was because I'm a newbie at this. – Martin Lehmannif
: in Clojure each conditional branch is an expression that returns a value. – glts