3

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.

2 Answers 2

5

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")))))
Sign up to request clarification or add additional context in comments.

1 Comment

You are, right! Looked at the (ok) source and figured that 2 minutes before your answer ... Thanks!
1

If you want to display image, you better return HTML page with <img> linked to your dir with static assets (usually resources/).

If you want to download file, here is an example of Compojure-api handler:

(ns example.app
  (:require [clojure.java.io :as io]
            [ring.util.response :as r]))

(defn file-download [name]
  (let [filename (str "resources/" name)
        bin (io/file filename)]
  (-> (r/response (io/input-stream bin))
      (r/header "Content-Type" "application/octet-stream")
      (r/header "Content-Disposition" "attachment")
      (r/header "Content-Length" (.length bin))
      (r/status 200))))

Adapt and use this function in place where your let goes in endpoint, hope it helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.