ring

petr.mensik 2017-11-29T01:51:01.000059Z

ok, after some digging I manage to fix it with following middleware configuration. However it now returns octet-stream for all JSON responses, is there a way to fix it? I also would be grateful for some kind of explanation of what's going on here (I realize it's because wrap-content middleware but I don't get why Ring strip content-type header from static content if I omit it - so how can I have both?)

(def app
  (api
   (assoc api-config
          :middleware
          [[wrap-cors :access-control-allow-origin [#"\\*"] :access-control-allow-methods [:get :post :put :delete :patch]]
           wrap-reload
           [wrap-defaults (-> site-defaults
                              (assoc-in [:responses :not-modified-responses] true)
                              (assoc-in [:static :resources] resources-root)
                              (assoc-in [:security :anti-forgery] false)
                              (assoc-in [:security :frame-options] :deny)
                              (assoc-in [:security :ssl-redirect] (not (:dev env)))
                              (assoc-in [:security :hsts] (not (:dev env))))]])

seancorfield 2017-11-29T02:26:40.000174Z

@petr.mensik What are you using that expects middleware in that format? I would expect wrap-cors, wrap-reload, and wrap-defaults all to be function calls threaded together around a handler...

seancorfield 2017-11-29T02:33:36.000235Z

The "simple" answer is that wrap-content-type doesn't do what many people expect it to: it sets the content type based on the requested :uri rather than anything intrinsic to the data you are returning.

seancorfield 2017-11-29T02:38:14.000232Z

You need wrap-json-response on the inside, which will set Content-Type: application/json; charset=utf-8 if it gets a response containing :body that satisfies coll?

seancorfield 2017-11-29T02:38:42.000249Z

If Content-Type is already set before wrap-content-type (in ring-defaults) gets control, it'll just pass that through.

seancorfield 2017-11-29T02:42:03.000044Z

We tend to have

(-> handler
    (wrap-json-response)
    (wrap-defaults config)
    (wrap-json-params)
    (cors))
so that CORS goes first on requests, then json params, then defaults, then your handler (as wrap-json-response does nothing on the way in). When your handler responds, json response goes first, then defaults, then CORS (since wrap-json-params does nothing on the way out).

seancorfield 2017-11-29T02:42:44.000049Z

Ring middleware is often very order-sensitive.

seancorfield 2017-11-29T02:45:05.000143Z

It looks like you have two questions in there (based on the above code and your SO question):

seancorfield 2017-11-29T02:45:36.000056Z

1. why is my JSON response not getting application/json content type? A: you don't have wrap-json-response in your code above.

seancorfield 2017-11-29T02:46:37.000071Z

2. why is resource-response producing application/octet-stream? A: because it doesn't set a content type at all, and then wrap-content-type tries to deduce it from the incoming URI (which has no "extension").

seancorfield 2017-11-29T02:46:51.000243Z

Hope that helps @petr.mensik?

petr.mensik 2017-11-29T11:37:32.000154Z

@seancorfield I am using Compojure-API which wraps middleware inside it's app configuration. Your response is definitely helpful, thanks a lot. However Compojure-API uses Muuntaja for content negotiation (which replaces ring-json I guess) and the content type is not recognized even if I use muuntaja.middleware/wrap-format as a first middleware (which is suppose to handle application/json response)

ikitommi 2017-11-29T13:43:34.000100Z

@petr.mensik hi, I can take a look at that.

petr.mensik 2017-11-29T13:45:32.000533Z

@ikitommi thanks a lot, this issue causes me a serious headache right now 😄

ikitommi 2017-11-29T14:06:23.000127Z

@petr.mensik so, what was the actual problem you had?

ikitommi 2017-11-29T14:38:32.000217Z

By default, Muuntaja returns Streams, you can slurp the :body to get the actual JSON. Or you can configure to use the streaming JSON, in which case you get a lazy muuntaja.protocols.StreamableResponse.

petr.mensik 2017-11-29T14:53:25.000206Z

1 All the endpoints returns something like (ok {:thing "another"}) 2. Application returns index.html on GET / which is a SPA downloading and linking other resources (this works well) 3. Routes are configured as in app above. So the problem here is that my responses are not explicitly converted to JSON and don't contain Content Type header like in your example? Is there a way how to automate that with Muuntaja?

ikitommi 2017-11-29T15:04:30.000691Z

oh, I see. missed the content-type. It seems that ring-defaults does something odd here. If you run it outside of api, it seems do do better.

ikitommi 2017-11-29T15:04:50.000570Z

does that look better?

ikitommi 2017-11-29T15:06:41.000528Z

My guess is that ring-defaults sets the content-type and Muuntaja is smart/dumb enough to respect the user defined content-type and thus, does not override it.

petr.mensik 2017-11-29T15:10:42.000258Z

Yes, that's the result I want - but that's kinda the problem, if I say (assoc-in [:responses :content-type] false) then the adding content-type for static resources stops working. However it adds application/json for the API

ikitommi 2017-11-29T15:12:14.000544Z

I would serve the static resources as a route in Compojure.

ikitommi 2017-11-29T15:13:22.000752Z

that should work.

petr.mensik 2017-11-29T15:14:38.000489Z

thats what I have right now, have I? https://pastebin.com/rjgKZsFz

ikitommi 2017-11-29T15:17:14.000270Z

Oh, but you have also the static resources defined via ring-defaults?

petr.mensik 2017-11-29T15:17:51.000377Z

Yes, I have to because resources-root differens between dev and production

petr.mensik 2017-11-29T15:18:06.000300Z

I wouldn't mess with that otherwise

petr.mensik 2017-11-29T15:18:45.000392Z

actually I used Noir before so I hoped it can work basically the same way with Compojure API https://github.com/noir-clojure/lib-noir/blob/master/src/noir/util/middleware.clj#L140

ikitommi 2017-11-29T15:21:07.000124Z

Not 100% sure, but I would guess that the ring-defaults mw is now serving your static resources and the compojure resource route in the end IS never called.

petr.mensik 2017-11-29T15:30:12.000908Z

So what do you think is the best solution which should work?

ikitommi 2017-11-29T15:41:16.000063Z

so, my 17:04 snippet doesn't work?

ikitommi 2017-11-29T15:45:16.000375Z

in it, the muuntaja & ring-defaults are in different order: muuntaja sets the json etc & the defaults should guess the content-type from the resources.

petr.mensik 2017-11-29T15:46:23.000156Z

I am gonna try it again, thx

ikitommi 2017-11-29T18:00:57.000207Z

btw, ring-defaults seems to add quite a lot of overhead to the request processing.