Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static files with clojure and ring

Tags:

clojure

ring

I am building a test clojure/ring project to learn how it works. I created an app I call "junkapp" and it has really one handler

(defn handler [request]
  {:status 200
  :headers {"Content-type" "text/html"}
  :body "Hello World"})

And also one call to wrap-resource for static content

(def app
  (wrap-resource handler "public"))

So then in my project.clj I have a reference to lein-ring and also set :handler to my junkapp.core/app

:plugins [[lein-ring "0.8.5"]]
:ring {:handler junkapp.core/app}

when I run this with lein run, everything works as expected. A call to / returns "Hello World" and a call to /test.html returns the contents of resources/public/test.html.

But then I tried to build it into a war file with

lein ring uberwar junkapp.war

and put it under the webapps/ dir of a tomcat7 server. Now when I go to any path under junkapp (so /junkapp/, /junkapp/foo, /junkapp/test.html) it always returns "Hello World" and I can't seem to make it reference the static content at all. In googling I see people just saying to use compojure.route/resources but as I am learning I'd like it to work like this and then add in more libraries later. What's going on here?

like image 702
MichaelB Avatar asked Jun 26 '13 15:06

MichaelB


2 Answers

I think what's happening here is that there's some code in wrap-resources here, specifically this line:

(or ((head/wrap-head #(resource-request % root-path)) request)
  (handler request))))

What happens is that when built as a war file, it doesn't understand that WEB-INF/classes/ is the root of the path it should be using to serve up the static content. So it's looking for public/test.html somewhere else (perhaps the root of the .war?) and so this "or" is false so it falls through to calling the handler directly.

I am not sure of a fix for this as I am not totally sure the internal working of how tomcat is handling this internally... that is, I dont know where it is looking to find the base path.

like image 151
MichaelB Avatar answered Nov 01 '22 07:11

MichaelB


From my handler.clj (I'm using compojure and lib-noir)

; defroutes and route/* being from Compojure
(defroutes app-routes
  (route/resources "/")
  (route/not-found "Not Found"))

(def all-routes [admin-routes home-routes blog-routes app-routes])
(def app (-> (apply routes all-routes)
; etc. etc.
(def war-handler (middleware/war-handler app))

Now I don't know the particulars of how WARs are expected to behave, but lets note this war-handler from lib-noir:

(defn war-handler
  "wraps the app-handler in middleware needed for WAR deployment:
  - wrap-resource
  - wrap-file-info
  - wrap-base-url"
  [app-handler]
  (-> app-handler
      (wrap-resource "public")
      (wrap-file-info)
      (wrap-base-url)))

Example application CMS: https://github.com/bitemyapp/neubite/

Compojure (routing): https://github.com/weavejester/compojure

Bag of useful utilities harvested post-Noir: https://github.com/noir-clojure/lib-noir

like image 39
Chris Allen Avatar answered Nov 01 '22 09:11

Chris Allen