luminus-framework / luminus-template

a template project for the Luminus framework
http://www.luminusweb.net/
MIT License
647 stars 147 forks source link

Problem with JSON responses. #59

Closed vbauer closed 10 years ago

vbauer commented 10 years ago

I've created project from template and tried to add some REST API method.:

(defroutes home-routes
  (GET "/tags" [] (db/find-tags-info)))

Method (db/find-tags-info) works fine and was tested. During processing response I have an exceptions:

java.lang.IllegalArgumentException: Key must be integer
             APersistentVector.java:265 clojure.lang.APersistentVector.invoke
                        response.clj:27 compojure.response/eval1340[fn]
                        response.clj:10 compojure.response/eval1301[fn]
                            core.clj:94 compojure.core/make-route[fn]
                            core.clj:40 compojure.core/if-route[fn]
                            core.clj:25 compojure.core/if-method[fn]
                           core.clj:107 compojure.core/routing[fn]
                          core.clj:2443 clojure.core/some
                           core.clj:107 compojure.core/routing
                        RestFn.java:139 clojure.lang.RestFn.applyTo
                           core.clj:619 clojure.core/apply
                           core.clj:112 compojure.core/routes[fn]
                           core.clj:107 compojure.core/routing[fn]
                          core.clj:2443 clojure.core/some
                           core.clj:107 compojure.core/routing
                        RestFn.java:139 clojure.lang.RestFn.applyTo
                           core.clj:619 clojure.core/apply
                           core.clj:112 compojure.core/routes[fn]
                      middleware.clj:44 noir.util.middleware/wrap-request-map[fn]
                  keyword_params.clj:32 ring.middleware.keyword-params/wrap-keyword-params[fn]
                   nested_params.clj:70 ring.middleware.nested-params/wrap-nested-params[fn]
                          params.clj:58 ring.middleware.params/wrap-params[fn]
                      middleware.clj:12 hiccup.middleware/wrap-base-url[fn]
                   format_params.clj:98 ring.middleware.format-params/wrap-format-params[fn]
                   format_params.clj:98 ring.middleware.format-params/wrap-format-params[fn]
                format_response.clj:113 ring.middleware.format-response/wrap-format-response[fn]
               multipart_params.clj:107 ring.middleware.multipart-params/wrap-multipart-params[fn]
                     validation.clj:140 noir.validation/wrap-noir-validation[fn]
                         cookies.clj:72 noir.cookies/noir-cookies[fn]
                        cookies.clj:171 ring.middleware.cookies/wrap-cookies[fn]
                        session.clj:142 noir.session/noir-flash[fn]
                           flash.clj:31 ring.middleware.flash/wrap-flash[fn]
                         session.clj:97 noir.session/noir-session[fn]
                         session.clj:85 ring.middleware.session/wrap-session[fn]
                           Var.java:415 clojure.lang.Var.invoke
                          reload.clj:18 ring.middleware.reload/wrap-reload[fn]
                      stacktrace.clj:17 ring.middleware.stacktrace/wrap-stacktrace-log[fn]
                      stacktrace.clj:80 ring.middleware.stacktrace/wrap-stacktrace-web[fn]
                           jetty.clj:18 ring.adapter.jetty/proxy-handler[fn]
                       (Unknown Source) ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$0.handle
                HandlerWrapper.java:116 org.eclipse.jetty.server.handler.HandlerWrapper.handle
                        Server.java:363 org.eclipse.jetty.server.Server.handle
        AbstractHttpConnection.java:483 org.eclipse.jetty.server.AbstractHttpConnection.handleRequest
        AbstractHttpConnection.java:920 org.eclipse.jetty.server.AbstractHttpConnection.headerComplete
        AbstractHttpConnection.java:982 org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete
                    HttpParser.java:635 org.eclipse.jetty.http.HttpParser.parseNext
                    HttpParser.java:235 org.eclipse.jetty.http.HttpParser.parseAvailable
            AsyncHttpConnection.java:82 org.eclipse.jetty.server.AsyncHttpConnection.handle
         SelectChannelEndPoint.java:628 org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle
          SelectChannelEndPoint.java:52 org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run
              QueuedThreadPool.java:608 org.eclipse.jetty.util.thread.QueuedThreadPool.runJob
              QueuedThreadPool.java:543 org.eclipse.jetty.util.thread.QueuedThreadPool$3.run
                        Thread.java:744 java.lang.Thread.run

But it works fine, if I wrap response using noir.response "json" method:

(defroutes home-routes
  (GET "/tags" [] (response/json (db/find-tags-info))))

I have the following App configuration:

(def app
  (middleware/app-handler
    ;; add your application routes here
    [home-routes app-routes]
    ;; add custom middleware here
    :middleware []
    ;; add access rules here
    :access-rules []
    ;; serialize/deserialize the following data formats
    ;; available formats:
    ;; :json :json-kw :yaml :yaml-kw :edn :yaml-in-html
    :formats [:json-kw :edn]))

Could you please help me? I thought it should work out of box (:formats [:json-kw :edn])

yogthos commented 10 years ago

Currently, the :formats key is only used for deserializing the request body. The response still needs to be serialized explicitly.

vbauer commented 10 years ago

Thank you for information!

FYI:

I have the same problem when I use custom middleware to serialize response body into JSON:

(defn json-response-middleware [handler]
  (fn [request]
    (let [response (handler request)]
      (response/json (:body response)))))

It works fine only with "defroutes".

yogthos commented 10 years ago

I did a bit of digging and looks like the ring-middleware-format library that lib-noir uses to handle formats expects the response to use the :body key:

(GET "/json" [] {:body {:foo "bar"}})

This should get serialized as expected without having to use response/json or custom middleware.

vbauer commented 10 years ago

Yes, it works, thank you. Maybe it'll be better to add a simple example in template (for preventing silly questions like this)?

yogthos commented 10 years ago

I updated the docs on the site, and I'll take a look at adding a sample route in the template.