10

I am trying to figure out why Ring's resource-response is choosing to respond with an application/octet-stream content type. I recently updated some sample code, that I've been learning from, so that it uses the newer ring-defaults. Prior to using ring-defaults, this code responded with the html content type. Why is this now choosing octet-stream?

(ns replays.handler
  (:require [compojure.core :refer [GET defroutes]]
            [compojure.route :as route]
            [ring.util.response :refer [response resource-response]]
            [ring.middleware.json :as middleware]
            [ring.middleware.defaults :refer [wrap-defaults api-defaults]]))

(defroutes app-routes
  (GET  "/" [] (resource-response "index.html" {:root "public"}))
  (GET  "/widgets" [] (response [{:name "Widget 1"} {:name "Widget 2"}]))
  (route/resources "/public")
  (route/not-found "not found"))

(def app
  (-> app-routes
      (middleware/wrap-json-body)
      (middleware/wrap-json-response)
      (wrap-defaults api-defaults)))

And, for version numbers, here's the project file ...

(defproject replays "0.1.0-SNAPSHOT"

  :url "http://example.com/FIXME"
  :description "FIXME: write description"

  :plugins [[lein-pdo "0.1.1"]
            [lein-ring "0.9.3"]
            [lein-cljsbuild "1.0.5"]]

  :dependencies [[org.clojure/clojure "1.6.0"]
                 [org.clojure/core.async "0.1.346.0-17112a-alpha"]
                 [org.clojure/clojurescript "0.0-3126"]
                 [org.omcljs/om "0.8.8"]
                 [ring/ring-core "1.3.2"]
                 [ring/ring-json "0.3.1"]
                 [ring/ring-defaults "0.1.4"]
                 [compojure "1.3.2"]
                 [cljs-http "0.1.29"]]

  :source-paths ["src/clj"]

  :ring {:handler replays.handler/app}

  :cljsbuild {:builds [{:id "dev"
                        :source-paths ["src/cljs"]
                        :compiler {:output-to "resources/public/js/app.js"
                                   :output-dir "resources/public/js/out"
                                   :optimizations :none
                                   :source-map true}}]}

  :aliases {"up" ["pdo" "cljsbuild" "auto" "dev," "ring" "server-headless"]}

  :profiles {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                                  [ring-mock "0.1.5"]]}})
Ryan
  • 7,733
  • 10
  • 61
  • 106
  • 3
    I'm not sure, but I think it has to do with the fact that [`wrap-content-type`](https://ring-clojure.github.io/ring/ring.middleware.content-type.html) uses the extension on your URI to set the content type headers? It can be worked around by adding an extension to the URI, or by manually setting the content type `(assoc-in (resource-response ...) [:headers "Content-Type"] "text/html")` – noisesmith Mar 30 '15 at 04:37
  • 1
    I believe your suggestion is the appropriate way of doing this. There's a provided function that does something similar, as well: http://ring-clojure.github.io/ring/ring.util.response.html#var-content-type – Ryan Mar 31 '15 at 23:34
  • Yeah, I avoided making an answer because while what I suggest will work, it also feels like doing by hand what the middleware should be doing for you. But if you look at that function's source, it calls header, and header does pretty much exactly what I put above. – noisesmith Apr 01 '15 at 00:01
  • And it's not as much an answer as a workaround - I'm not sure of the why. – noisesmith Apr 01 '15 at 00:03
  • I had the same issue and it was confirmed by trying a URL with a `.htm` extension, this gave a `text/html` content-type is the response. Same for `.rss`, it gives `application/rss+xml`. – Kris Feb 15 '17 at 16:37

1 Answers1

10

This is happening because api-defaults (actually it's ring.middleware.content-type) tries to guess your content-type based on the data you passed to it. It uses file extensions to do that and since you are using resource-response, there is no extension. So by default ring.middleware.content-type uses application/octet-stream.

The solution is to provide your content-type to the response like below:

(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"))
fasfsfgs
  • 956
  • 10
  • 16