xeqi / peridot

a basic api for interacting with ring apps
145 stars 20 forks source link

POST requests that aren't x-www-form-urlencoded? #22

Closed magnars closed 9 years ago

magnars commented 9 years ago

Hello! Thanks for creating this nice tool. I'm using it to test an API that takes EDN in the post body. I see that the body defmulti has support for passing a String and it being served to Ring as a ByteArrayInputStream, but the request method itself foils it by adding content-type application/x-www-form-urlencoded (and the multipart/multipart? fails if you don't pass it a map).

Is there any support for POSTing non-urlencoded with peridot? Any thoughts on how to do it?

magnars commented 9 years ago

To fix this for myself, I added this:

(defn handle-edn-body [request]
  (if-let [edn (:edn-body request)]
    (-> request
        (mock/body (pr-str edn))
        (mock/content-type "application/edn"))
    request))

and then hijacked peridot.core/request and changed

(let [request (pr/build uri env headers cookie-jar content-type)

to

(let [request (handle-edn-body (pr/build uri env headers cookie-jar content-type))

Which seems to be working fine. Maybe support for something along these lines would be interesting to bring into peridot itself?

glenjamin commented 9 years ago

I think you should be able to do this by passing the :content-type key into the (request) function as seen in https://github.com/xeqi/peridot/blob/master/test/peridot/test/core.clj#L83

I'll leave this issue open as we could do with better docs for what (request) supports

magnars commented 9 years ago

Yes, you can set your own content-type ahead of time, and it won't be overwritten, but multipart/multipart? still throws an exception for non-map :params

glenjamin commented 9 years ago

I think I'm missing something, this test passes: 59f37ae

The only way multipart/multipart? would receive non-map :params would be if it was passed explicitly in (request).

Can you provide a fuller example?

magnars commented 9 years ago

Ah, look at that. I tried posting it as :params, not :body. I couldn't see the :body added to the request anywhere in peridot.request/build. I now see it is added along with most everything else in add-env. Thanks!

mkorvas commented 9 years ago

I know this ticket is closed -- but I have run into the exact problem that the title of this issue implies. I tried to construct and send to the app a POST request with Content-Type: application/json but was getting rejected by the handler with 415 Unsupported Media Type.

Reading the source a few times (I am new to Clojure...) and seeing this discussion, I figured that to send such a request, I have to say not just

(request uri
         :request-method :post
         :content-type "application/json"
         :body some-json-string)

(headers ended up being only {"host" "localhost"}) or

(request uri
         :request-method :post
         :headers {"content-type" "application/json"}
         :body some-json-string)

(headers got overwritten to {"host" "localhost", "content-type" "application/x-www-form-urlencoded"}) but I must include both :headers and :content-type:

(request uri
         :request-method :post
         :headers {"content-type" "application/json"}
         :content-type "application/json"
         :body some-json-string)

I was reluctant to call (content-type "application/json") in the "thread" as the docs say it makes the setting persistent also for following requests which I certainly didn't want... although I now realize that specifying :content-type "application/json" in the (request) arguments probably has the same effect.

This wasn't really clear from the docs, so I would find it nice to mention this feature there. Even nicer would be to be able to ask for removing the :content-type key from the session upon issuing the next request to the app -- not having the :content-type setting persist for all future requests. (I will be glad if you tell me I missed something and this behaviour is already there!)

glenjamin commented 9 years ago

Good spot @mkorvas - this is now fixed in 4c83ef45bac4eb2b2a316415a197395641ac0294 and relased as 0.4.1.

Passing :content-type now hits the same code-path as using (content-type) when building a request.

mkorvas commented 9 years ago

Great, thanks! I can confirm that with peridot==0.4.1, just specifying :content-type "application/json" works for me.