I am trying to host static assets along with services in Pedestal 0.5.1. I am using the ::file-path
to point to a directory to host the files. This works fine if I navigate directly to the file http://localhost:8888/index.html but if I go to the root of the site http://localhost:8888 it serves the files as application/octet-stream
rather than text/html
. I adapted the Hello World Sample and it has the same behavior.
src/hello_world/server.clj
(ns hello-world.server
(:require [io.pedestal.http :as http]
[io.pedestal.http.route :as route])
(:gen-class))
(def routes
(route/expand-routes [[]]))
(def service
{:env :prod
::http/join? false
::http/routes routes
::http/file-path "/tmp/www"
::http/type :jetty
::http/allowed-origins {:creds true :allowed-origins (constantly true)}
::http/port 8888})
(defonce runnable-service (http/create-server service))
(defn -main
"The entry-point for 'lein run'"
[& args]
(println "\nCreating your server...")
(http/start runnable-service))
Start lein run
$ curl -i localhost:8888
HTTP/1.1 200 OK
Date: Fri, 18 Nov 2016 16:02:56 GMT
Last-Modified: Fri, 18 Nov 2016 15:10:22 GMT
Content-Type: application/octet-stream
Content-Length: 12
Server: Jetty(9.3.8.v20160314)
hello world
$ curl -i localhost:8888/index.html
HTTP/1.1 200 OK
Date: Fri, 18 Nov 2016 16:03:02 GMT
Last-Modified: Fri, 18 Nov 2016 15:10:22 GMT
Content-Type: text/html
Content-Length: 12
Server: Jetty(9.3.8.v20160314)
hello world
Is there some way to fix the "/" route to serve the correct content-type?
To get correct content-types for files served as directory indexes,
add the interceptor io.pedestal.http.ring-middlewares/file-info
to
your Pedestal configuration.
This requires that you override the default interceptor chain with your own, so you will have to include all of the default interceptors that your app needs.
For example, your service might look something like this:
(ns hello-world.service
(:require
[io.pedestal.http :as http]
[io.pedestal.http.ring-middlewares :as middlewares]
[io.pedestal.http.route :as route]
[io.pedestal.http.route.definition :refer [defroutes]]))
(defroutes routes
[[]])
(def service
{::http/type :jetty
::http/port 8080
::http/interceptors [http/log-request
http/not-found
middlewares/session
route/query-params
(middlewares/file-info) ; HERE
(middlewares/file "/tmp/www")
;; ... insert other interceptors ...
(route/router #(deref #'routes) :map-tree)]})
For examples of other default interceptors you might want to include, see default-interceptors.
This probably does not come up very often in practice because many web applications use a handler function to generate the home page instead of returning a static file.
For an alternate solution, you could write a route handler for the /
route which returns the contents of index.html
with the appropriate
content-type.
Pedestal's default interceptor stack includes io.pedestal.http.ring-middlewares/file and io.pedestal.http.ring-middlewares/content-type.
These interceptors just wrap the Ring middleware functions file-request and content-type-response, respectively.
file-request
returns a java.io.File
object as the HTTP response.
content-type-response
examines the request URI to determine the
value of the Content-Type header. Since the URI is just /
it
defaults to application/octet-stream
.
By contrast ring.middleware.file-info
(which is deprecated) examines
path of the actual File
object in the response.
See file-info-response.
io.pedestal.http.ring-middlewares/file-info
is the interceptor
wrapper around ring.middleware.file-info/file-info-response
.