Brief introduction to Compojure
Compojure gives you a shortcut to make a monofunctional web application. For example a Lingr bot is a web application that only need to be responsible with one single endpoint that handles POST request. The below is a web application that only shows "hello" in / endpoint with GET request.
(defroutes hello
(GET "/" [] "hello"))
(run-jetty hello {:port 80})
Note that it requires you to be the root of the system if you are going to run a web app on port 80.
The main part of the app is just 3 lines of code. That reminds me of code examples for Sinatra, a Ruby web library.
get '/' do
'hello'
end
Anyways the Compojure example code doesn't work only with the main logic. You are supposed to make a Leiningen project usually to manage the app and its dependent libraries.
$ lein new hello
$ cd hello
project.clj
(defproject hello "1.0.0-SNAPSHOT"
:main hello.core
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.2.1"]
[compojure "1.0.0-RC2"]
[ring "1.0.1"]])
src/hello/core.clj
(ns hello.core
(:use
compojure.core
ring.adapter.jetty))
(defroutes hello
(GET "/" [] "hello"))
(run-jetty hello {:port 80})
then
$ lein deps
$ lein run
it should work.
Parameters
(defroutes hello
(GET "/" [] "hello"))
(run-jetty hello {:port 80})
The 2nd argument of GET, []
in this case, is parameter list for the expression you give in 3rd argument, which mostly for referring GET parameters. That's actually a hashmap that contains :params
key which value is also a hashmap of GET parameters. Ditto in POST.
How can we get the raw post parameter?
(POST "/" {params :params} (...))
In that way you cannot get raw data because it's after the process. You can reconstruct the raw data only when the given parameter is like proper a=1\nb=2
form. These days some web apis are required to handle raw POST data, which is mostly in JSON, like a Lingr Bot API.
The answer is in :body of the parameter, but it's not a String but a mysterious HttpParser.Input object, assuming you are using ring as the middleware.
http://jetty.codehaus.org/jetty/jetty-6/apidocs/org/mortbay/jetty/HttpParser.Input.html
This class looks weird because even though this has read()
method the return value type isn't String but int. The other read()
looks like you are supposed to pass a mutable data and refer the changed data.
Fortunately we can use slurp
Clojure function to hide this complicated behaviour.
(defroutes hello
(POST "/" {body :body} (slurp body)))
This shows the given raw POST parameter!
Great, thanks for posting!
ReplyDeleteIn the defroutes above, where you write "{body :body}", is body an instance of HttpParser.Input?
ReplyDeleteyes
ReplyDeleteIt was org.mortbay.jetty.HttpParser$Input
Thanks for posting, this saved me a lot of time, thanks!
ReplyDelete