Search code examples
imageclojureresponsecompojure

Compojure trouble displaying an image


I've been struggling with this for over two days now and don't seem to find a solution.

So all I am trying to do is to display an image in the browser, but when I am calling the endpoint this is what happens:

Cannot JSON encode object of class: class java.io.File

Endpoint

(context "/servers" []
           :datomic true

           (GET "/:id/graph/:panel-type" {db :db user :user}
                :summary "Return a server graph image"
                :path-params [id :- Long
                              panel-type :- String]
                (let [file-path (str panel-type ".png")

                      result (-> (response/response (clojure.java.io/file file-path))
                                 (response/content-type "image/png")))]

                  (ok result)))) ;; ring.util.http-response

I am new to compojure API, but something makes me think there is a problem with middleware implementation - custom formats?

Middleware

(defn wrap-formats [handler]
  (let [wrapped (wrap-restful-format
                  handler
                  {:formats [:json-kw :transit-json :transit-msgpack]})]
    (fn [request]
      ;; disable wrap-formats for websockets
      ;; since they're not compatible with this middleware
      ((if (:websocket? request) handler wrapped) request))))

Stacktrace

com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class java.io.File: cpu.png
at cheshire.generate$generate.invokeStatic(generate.clj:152)
at cheshire.generate$generate.invoke(generate.clj:116)
at cheshire.generate$generate.invokeStatic(generate.clj:122)
at cheshire.generate$generate.invoke(generate.clj:116)
at cheshire.core$generate_string.invokeStatic(core.clj:74)
at cheshire.core$generate_string.invoke(core.clj:49)
at ring.middleware.format_response$make_json_encoder$fn__20198.invoke(format_response.clj:221)
at ring.middleware.format_response$wrap_format_response$fn__20186.invoke(format_response.clj:204)
at ring.middleware.keyword_params$wrap_keyword_params$fn__4302.invoke(keyword_params.clj:36)
at ring.middleware.nested_params$wrap_nested_params$fn__20350.invoke(nested_params.clj:89)
at ring.middleware.params$wrap_params$fn__4376.invoke(params.clj:67)
at compojure.api.middleware$wrap_options$fn__23551.invoke(middleware.clj:74)
at compojure.api.routes.Route.invoke(routes.clj:74)
at clojure.lang.Var.invoke(Var.java:381)
at compojure.core$routing$fn__4157.invoke(core.clj:185)
at clojure.core$some.invokeStatic(core.clj:2674)
at clojure.core$some.invoke(core.clj:2665)
at compojure.core$routing.invokeStatic(core.clj:185)
at compojure.core$routing.doInvoke(core.clj:182)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$apply.invoke(core.clj:652)
at compojure.core$routes$fn__4161.invoke(core.clj:192)
at clojure.lang.Var.invoke(Var.java:381)
at ring.middleware.reload$wrap_reload$fn__46696.invoke(reload.clj:39)
at selmer.middleware$wrap_error_page$fn__46709.invoke(middleware.clj:9)
at prone.middleware$wrap_exceptions$fn__46879.invoke(middleware.clj:126)
at buddy.auth.middleware$wrap_authentication$fn__44919.invoke(middleware.clj:42)
at buddy.auth.middleware$wrap_authorization$fn__44927.invoke(middleware.clj:94)
at ring.middleware.cors$handle_cors.invokeStatic(cors.clj:146)
at ring.middleware.cors$handle_cors.invoke(cors.clj:135)
at ring.middleware.cors$wrap_cors$fn__45217.invoke(cors.clj:160)
at ring.middleware.flash$wrap_flash$fn__45283.invoke(flash.clj:39)
at ring.middleware.session$wrap_session$fn__45465.invoke(session.clj:108)
at ring.middleware.keyword_params$wrap_keyword_params$fn__4302.invoke(keyword_params.clj:36)
at ring.middleware.nested_params$wrap_nested_params$fn__20350.invoke(nested_params.clj:89)
at ring.middleware.multipart_params$wrap_multipart_params$fn__45551.invoke(multipart_params.clj:172)
at ring.middleware.params$wrap_params$fn__4376.invoke(params.clj:67)
at ring.middleware.cookies$wrap_cookies$fn__45416.invoke(cookies.clj:175)
at ring.middleware.absolute_redirects$wrap_absolute_redirects$fn__45638.invoke(absolute_redirects.clj:47)
at ring.middleware.resource$wrap_resource$fn__45567.invoke(resource.clj:37)
at ring.middleware.content_type$wrap_content_type$fn__25387.invoke(content_type.clj:34)
at ring.middleware.default_charset$wrap_default_charset$fn__45610.invoke(default_charset.clj:31)
at ring.middleware.not_modified$wrap_not_modified$fn__25417.invoke(not_modified.clj:53)
at ring.middleware.x_headers$wrap_x_header$fn__45246.invoke(x_headers.clj:22)
at ring.middleware.x_headers$wrap_x_header$fn__45246.invoke(x_headers.clj:22)
at ring.middleware.x_headers$wrap_x_header$fn__45246.invoke(x_headers.clj:22)
at voltage.middleware$wrap_bearer_token$fn__46930.invoke(middleware.clj:72)
at ring.middleware.webjars$wrap_webjars$fn__45738.invoke(webjars.clj:40)
at voltage.middleware$wrap_internal_error$fn__46919.invoke(middleware.clj:37)
at immutant.web.internal.undertow$create_http_handler$reify__57634.handleRequest(undertow.clj:239)
at org.projectodd.wunderboss.web.undertow.async.websocket.UndertowWebsocket$2.handleRequest(UndertowWebsocket.java:107)
at io.undertow.server.session.SessionAttachmentHandler.handleRequest(SessionAttachmentHandler.java:68)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:802)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

Any clue on the above is highly appreciated.


Solution

  • The problem is that you're calling both ring.util.response/response and ring.util.http-response/ok:

    (ok (ring.util.response/response "foo"))
    => {:status 200, :headers {}, :body {:status 200, :headers {}, :body "foo"}}
    

    Using either of them separately works:

    (ns compojure.api.examples.handler
      (:require [clojure.java.io :as io]
                [compojure.api.sweet :refer :all]
                [ring.middleware.format :refer [wrap-restful-format]]
                [ring.util.http-response :refer :all]
                [ring.util.response :as response]
                [compojure.api.examples.domain :refer :all]
                [schema.core :as s]))
    
    (defn wrap-formats [handler]
      (let [wrapped (wrap-restful-format
                      handler
                      {:formats [:json-kw :transit-json :transit-msgpack]})]
        (fn [request]
          ;; disable wrap-formats for websockets
          ;; since they're not compatible with this middleware
          ((if (:websocket? request) handler wrapped) request))))
    
    (def app
      (wrap-formats
       (api
        (GET "/lisplogo" []
             (-> (response/response (io/file "lisplogo_256.png"))
                 (response/content-type "image/png")))
    
        (GET "/lisplogo2" []
             (assoc-in (ok (io/file "lisplogo_256.png"))
                       [:headers "Content-Type"]
                       "image/png")))))