According to the Compojure documentation on routes, I can easily get individual parameters like this:
(POST "/my-app" [param1 param2]
(str "<h1>Hello " param1 " and " param2 "</h1>"))
How do I get all parameters, not just individual parameters?
compojure handlers receive the entire request map as their argument, so handler has also an access to all of the parameters. For example, to see entire request:
(POST "/" request
(str request))
or, to extract all form parameters:
(POST "/" request
(str (:form-params request)))
The syntax used in the question is a compojure-specific destructuring syntax, which allows extracting individual parameters from the request. This is similar to clojure's usual destructuring syntax, and, as with usual destructuring, compjure's destructuring also allows mixing the destructuring and still getting the entire request:
(POST "/" [param1 param2 :as request]
(str (:form-params request)))
or, extracting named and all "additional" parameters:
(POST "/" [param1 param2 & more-params]
(str more-params))
I just guessed to put & params in the vector and that worked:
(POST "/my-app" [& params]
(str "<h1>Hello " params "</h1>"))
something like this return all the params:
(POST "/test" {params :params}
(str "POST params=" params))
use this notation to access a particular parameter:
(println (params :Nom))
Related
I have a server with an endpoint .../end2 which I send parameters to, such as:
.../end2?a=2&b=1
How do I get a map {:a 2 :b 1}? I thought (:params request) is the way to go but I get an empty map..
Assuming you're using compojure, the params are not automatically bound to the request, and ring middleware must be applied to do this:
(defroutes app-routes
(GET "/end2" request (str (:params request))))
(def app
(-> app-routes
ring.middleware.params/wrap-params))
(run-server #'app {:port 8888})
You need to add ring middle ware to parse params. You could check ring default
You don't have to worry about nested params or others.
I am writing a web API using Compojure with basic-authentication middleware. The basic authentication part looks something like:
(defn authenticated? [id pass]
(and (= id "blah")
(= pass "blah"))
id and pass are passed in using the id:pass#website technique. My problem is, I would like to access this id and pass further on down where the routes are processed with the various GET, PUT, POST, etc. headings. But I can't figure out how to do this, and haven't had any luck googling around.
; i'd like to access id and pass here
(defroutes routes
(GET "/" [] {:status 200 :headers {} :body "hi!"})
...)
I presume the solution is for the above to somehow "add" id and pass to some set of variables that can be accessed where the routes are processed, but I have no idea how to add nor to access them.
Hopefully someone can point me in the right direction - thanks.
Assuming you are talking about https://github.com/remvee/ring-basic-authentication, the authenticated? fn can return a truthly value that will be added to the request in the :basic-authentication. Something like (untested):
(defn authenticated? [id pass]
(and (= id "gal")
(= pass "foo")
{:user id :passwd pass}))
(defroutes routes
(GET "/" {the-user :basic-authentication} {:status 200 :headers {} :body (str "hi! Mr. " (:user the-user) " your password is " (:passwd the-user))})
...)
The return of the authenticated? method is associated in request map referenced by the key :basic-authentication. Here goes an example with a route that returns the user. You could, however, return a map or any other object and access it through :basic-authentication key.
(defn authenticated? [user password] user)
(defroutes require-auth-routes
(GET "/return-user" request (.toString (:basic-authentication request)))
(def my-app
(routes
(-> require-auth-routes
(wrap-basic-authentication authenticated?)))
org.clojure/clojure-contrib "1.2.0"
ring "1.1.8"
compojure "1.1.5"
clout "1.1.0"
(defroutes rest-routes
(GET "/" [] "<p> Hello </p>")
(POST "/api/v1/:stor/sync" [stor] (start-sync stor))
(POST ["/api/v1/:stor/:txn/data/:file" :file #".*"] [stor txn file] (txn-add stor txn file))
(ANY "*" [] "<p>Page not found. </p>"))
In the second POST, I also want to pass all http-headers to "txn-add" handler. I did lot of google and look through the code, but couldn't find anything useful.
I know, I can use the following to pass headers (but then it doesn't parse url request),
(POST "/api/v1"
{headers :headers} (txn-add "dummy stor" "dummy txn" headers))
Also, how do I pass the content (i.e. :body) of POST request to "txn-add" ?
If the second argument to GET, POST etc is not a vector, it's a destructuring binding form for request. That means you can do things like:
(GET "/my/path"
{:keys [headers params body] :as request}
(my-fn headers body request))
To pick out the parts of request you want. See the Ring SPEC and Clojure's docs on binding & destructuring
The whole request map can be specified in the bindings using :as keyword in bindings and then used to read headers or body :
(POST ["/api/v1/:stor/:txn/data/:file" :file #".*"]
[stor txn file :as req]
(my-handler stor txn file req))
My Compojure web app ([compojure "1.0.1"]) always receives an empty parameter map, despite adding wrap-params etc. Code sample below:
(defroutes public-routes
(PUT "/something" {params :params}
(println (str "Params: " params))
(do-put-something params)))
(def myapp
(-> public-routes
ring-params/wrap-params))
(defn start-server []
(future (jetty/run-jetty (var myapp) {:port 8080})))
I've tried adding the wrap-params, wrap-keyword-params and wrap-multipart-params but when I PUT to the endpoint using httpie (or my client), I find that params is always empty. Can anyone help?
Thanks!
The only problem with your example code was that it lacks a ring response hash-map in the route body. The solution is evaluate to a ring response instead of using println. When you call println in your route it prints to standard out where the server process is running, which has nothing to do with the response to the API call.
(defroutes public-routes
(PUT "/something" {params :params}
{:status 200
:body (str "Params: " params)}))
This produces a 200 response with Params: {"foo" "bar"} as the response body.
I am using this to test your PUT route:
curl -X PUT -d "foo=bar" http://127.0.0.1:8080/something
I'm trying to make any of the following mappings work to map http://mysite.org/add?http://sitetoadd.com or http://mysite.org/add?u=http://sitetoadd.com
(GET "/add?:url" [url] url)
(GET "/add?u=:url" [url] url)
(GET "/add" {params :params} (params :u))
(GET "/add" {params :params} (params "u"))
(GET "/add" [u] u)
But it just fails and I don't know why. On the other hand, this works:
(GET "/add/:url" [url] url)
but I can't use it because I have to pass a url and http://mysite.org/add/http://sitetoadd.com is invalid while http://mysite.org/add?http://sitetoadd.com is ok.
EDIT: dumping request i've seen that params is empty. I thought that it would contain POST and GET parameters, but the only place where I can find the params I pass is in :query-string ("u=asd"). It seems that a middleware is needed to parse query strings. My question stands still, by the way.
See https://github.com/weavejester/compojure under Breaking Changes. The params map is no longer bound by default. If you have your example routes inside a "(defroutes main-routes ... )", make sure you activate it through "(handler/site main-routes)" as explained on https://github.com/weavejester/compojure/wiki/Getting-Started as it is the site or api method that makes sure the params map gets bound by default.
Here's an example that works:
(ns hello-world
(:use compojure.core, ring.adapter.jetty)
(:require [compojure.route :as route]
[compojure.handler :as handler]))
(defroutes main-routes
(GET "/" {params :params} (str "<h1>Hello World</h1>" (pr-str params)))
(route/not-found "<h1>Page not found</h1>"))
(defn -main [& m]
(run-jetty (handler/site main-routes) {:port 8080}))