34

I have a static file called index.html that I'd like to serve when someone requests /. Usually web servers do this by default, but Compojure doesn't. How can I make Compojure serve index.html when someone requests /?

Here's the code I'm using for the static directory:

; match anything in the static dir at resources/public
(route/resources "/")
Kevin Burke
  • 61,194
  • 76
  • 188
  • 305

8 Answers8

41

An alternative could be to create either a redirect or a direct response in an additional route. Like so:

(ns compj-test.core
  (:use [compojure.core])
  (:require [compojure.route :as route]
            [ring.util.response :as resp]))

(defroutes main-routes
  (GET "/" [] (resp/file-response "index.html" {:root "public"}))
  (GET "/a" [] (resp/resource-response "index.html" {:root "public"}))
  (route/resources "/")
  (route/not-found "Page not found"))

The "/" route returns a file response of "index.html" which is present in the public folder. The "/a" route responds directly by 'inlineing' the file index.html.

More on ring responses: https://github.com/mmcgrana/ring/wiki/Creating-responses

EDIT: removed unnecessary [ring.adapter.jetty] import.

Dhruv Chandna
  • 571
  • 5
  • 17
Paul
  • 7,836
  • 2
  • 41
  • 48
  • 6
    Hi. (GET "/" [] (resp/resource-response "index.html" {:root "public"})) works perfectly for me. – joshua Dec 31 '12 at 22:50
  • Be careful with not providing the content-type you want. Another middleware such as `wrap-content-type` could break your code. I posted [an answer](http://stackoverflow.com/a/32491623/1707589) addressing this. – fasfsfgs Sep 10 '15 at 01:41
28
(ns compj-test.core
  (:use [compojure.core])
  (:require
        [ring.util.response :as resp]))

(defroutes main-routes
  (GET "/" [] (resp/redirect "/index.html")))

What you're asking for is a redirect from / to /index.html. Its as simple as (resp/redirect target). No need to over complicate things.

jandrewthompson
  • 281
  • 3
  • 2
25

This would be a pretty simple Ring middleware:

(defn wrap-dir-index [handler]
  (fn [req]
    (handler
     (update-in req [:uri]
                #(if (= "/" %) "/index.html" %)))))

Just wrap your routes with this function, and requests for / get transformed into requests for /index.html before the rest of your code sees them.

(def app (-> (routes (your-dynamic-routes)
                     (resources "/"))
             (...other wrappers...)
             (wrap-dir-index)))
hyperboreean
  • 8,273
  • 12
  • 61
  • 97
amalloy
  • 89,153
  • 8
  • 140
  • 205
  • 3
    I guess my point is: Compojure doesn't do this, because it's easy to do with Ring instead. Your Clojure web stack is not one all-encompassing platform, but a series of pluggable and specialized tools. Solve problem X with the tool that's designed for it. – amalloy Oct 11 '11 at 18:11
19

This works just fine. No need to write a ring middle-ware.

(:require [clojure.java.io :as io])

(defroutes app-routes 
(GET "/" [] (io/resource "public/index.html")))
Binita Bharati
  • 5,239
  • 1
  • 43
  • 24
  • Don't know if it answers the question but it helped me for what I needed! Upvoted! Thanks! – leontalbot Aug 05 '14 at 16:37
  • Be careful with not providing the content-type you want. Another middleware such as `wrap-content-type` could break your code. I posted [an answer](http://stackoverflow.com/a/32491623/1707589) addressing this. – fasfsfgs Sep 10 '15 at 01:40
13

After viewing a lot of the answers here, I'm using the following code:

(ns app.routes
  (:require [compojure.core :refer [defroutes GET]]
            [ring.util.response :as resp]))

(defroutes appRoutes
  ;; ...
  ;; your routes
  ;; ...
  (GET "/" []
       (resp/content-type (resp/resource-response "index.html" {:root "public"}) "text/html"))))
  • No redirects;
  • No need of another middleware;
  • index.html stays in the right folder (resources/public/index.html);
  • It works with other middleware (a lot of the answers break when using with some ring default middleware);
  • It provides content-type so it works with wrap-content-type middleware.

Check ring-defaults. It has best practices middleware you should use on your projects.

fasfsfgs
  • 956
  • 10
  • 16
3

Recently I discovered that @amalloy's answer doesn't work when the Clojure/Compojure app is run under Jetty or Tomcat as a .war. In this case :path-info needs to be updated. Also, I think this version will handle any route, not just the "root" route.

(defn- wrap-dir-index [handler]
  (fn [request]
    (handler
     (let [k (if (contains? request :path-info) :path-info :uri) v (get request k)]
       (if (re-find #"/$" v)
         (assoc request k (format "%sindex.html" v))
         request)))))

See also: https://groups.google.com/forum/#!msg/compojure/yzvpQVeQS3w/RNFkFJaAaYIJ

UPDATED: Replaced example with a version that works.

tvaughan
  • 341
  • 3
  • 5
1

when other code doesnt work, try this code.

(GET "/about/" [] (ring.util.response/content-type
                     (ring.util.response/resource-response "about/index.html" {:root "public"}) "text/html"))
0

Just a consideration Binita, a gotcha I have been experiencing. Despite I couldn't find any documentation regarding importance of order defining Compojure routes I find out that this doesn't work

(GET "/*" [] r/static) 
(GET "/" [] (clojure.java.io/resource "public/index.html"))

while this does work

(GET "/" [] (clojure.java.io/resource "public/index.html"))
(GET "/*" [] r/static) 

Obviously the * matchs also the empty string but I thought that the order shouldn't matter at all.

Jaime Agudo
  • 8,076
  • 4
  • 30
  • 35
  • Be careful with not providing the content-type you want. Another middleware such as `wrap-content-type` could break your code. I posted [an answer](http://stackoverflow.com/a/32491623/1707589) addressing this. – fasfsfgs Sep 10 '15 at 01:40