Http POST request with multipart/form-data using clj-ajax - ajax

I have an endpoint where I can upload a text file with curl like this:
curl -X POST -H "Content-Type: multipart/form-data" -F "file=#/resources/speciesDiffusion.tree" http://localhost:4000/continuous/tree
now I need to send a similar request from a browser, but
(ajax/ajax-request
{:uri (str "http://localhost:4000" "/continuous/tree")
:method :post
:params {:treefile file}
:handler #(println %1)
:format (ajax/text-request-format)
:response-format (ajax/json-response-format {:keywords? true})})
gives me a (nicely json converted, so I got that part going, which is nice) error response:
[false {:status 500, :status-text , :failure :error, :response {:timestamp 1494279686227, :status 500, :error Internal Server Error, :exception org.springframework.web.multipart.MultipartException, :message Current request is not a multipart request, :path /continuous/tree}}]
Also, in the browser I can see that the content-type headers is not correctly set, but I couldn't get it to work with any other combination of :format and :params.

There are some examples in the README of the cljs-ajax project. For example:
(let [form-data (doto
(js/FormData.)
(.append "id" "10")
(.append "file" js-file-value "filename.txt"))]
(POST "/send-file" {:body form-data
:response-format (raw-response-format)
:timeout 100}))
https://github.com/JulianBirch/cljs-ajax

As per my comment the problem was not in the request, but rather in the f-tion that dispatched it, i.e. I was reading the file content instead of sending the raw object like here:
(defn handle-file-upload [evt]
(let [target (.-currentTarget evt)
js-file (-> target .-files (aget 0))]
(do
(re-frame/dispatch [:post-file {:file js-file
:name (.-name js-file)}])
(set! (.-value target) ""))))
(defn upload-button []
(fn []
[:input {:class "none"
:id "file-upload"
:type "file"
:on-change #(handle-file-upload %)}]))
where
:post-file
is an event which invokes the handler which does the POST request.

Related

wrap-params (cljs-ajax + compojure)

Consider the following compojure routing:
(defroutes main-routes
(POST "/something" r {:body (prn-str (:params r))}))
(def handler
(-> main-routes
(wrap-params)))
When testing this with curl I'm getting the desired result:
curl -d "a=b" localhost:3000/something
{"a" "b"}
The Post parameters are read by compojure and wrapped to the params-map.
However this does not work with an ajax request initiated by the cljs-ajax library:
(POST "/something" {:handler #(js/alert %)
:params {"a" "b"}})
It alerts "{}". When changing the code to use GET, it works however. I guess this is due to the fact, that the browser sends the body as an input stream and not as plain text. But I'm not sure and I don't know how to fix this.
It looks like cljs-ajax is sending a transit-formatted request and response by default. (See :format and :response-format defaults here). You might try specifying an explicit json response in the request map -
{:handler #(js/alert %)
:params {"a" "b"}
:response-format :json}

clearing a session (logging a user out) in clojure-ring

With the impression that simply setting the request-map's :session to nil will cause a logout, my code looks like the following:
(GET "/logout" [ :as request]
(if-let [useremail (get-in request [:session :ph-auth-email])]
(-> (response {:status 200,
:body (pr-str "logged out " useremail),
:headers {"Content-Type:" "text/html"}})
(assoc request [:session nil]))))
But I get an error:
java.lang.Thread.run(Thread.java:745)
2015-02-18 09:29:05.134:WARN:oejs.AbstractHttpConnection:/logout
java.lang.Exception: Unrecognized body: {:status 200, :body "\"logged out \" \"sova\"", :headers {"Content-Type:" "text/html"}}
ring.util.response/response expects only the body as parameter since it'll build :status and :headers around it (see here). A map, however, is not a valid body - only strings, files, streams are allowed.
So, this is what causes the exception; now, on to your question: You can log out a user by setting :session to nil in your response (source) - which reduces your code to:
(GET "/logout" [:as request]
(if-let [useremail (get-in request [:session :ph-auth-email])]
{:status 200,
:body (pr-str "logged out " useremail),
:session nil, ;; !!!
:headers {"Content-Type" "text/html"}}))

How do I PUT a file with Typheous?

I am trying to send a file via a HTTP PUT request. Curl allows this like:
http://curl.haxx.se/docs/httpscripting.html#PUT
What's the correct way of doing this with Typheous?
FWIW this is what I think was the complete (but not necessarily shortest) answer to the question.
Curl allows the uploading of files w/ PUT; the invocation is:
$ curl --upload-file filename url
where url may be something like:
http://someurl/script.php?var=value&anothervar=val&...
Typhoeus provides the same functionality, but the right way to pass url, params and their values as well as the file body is buried in the ethon docs:
request = Typhoeus::Request.new(
url, :method => :put, :params => params_hash,
:body => File.open(filename) { |io| io.read })
Use request object to get response, etc.
You couldn't have looked very hard:
Examples:
Make put request.
Typhoeus.put("www.example.com")
Parameters:
base_url (String) — The url to request.
options (options) (defaults to: {}) — The options.
Options Hash (options):
:params (Hash) — Params hash which is attached to the base_url.
:body (Hash) — Body hash which becomes a PUT request body.
http://rubydoc.info/github/typhoeus/typhoeus/Typhoeus/Request/Actions#put-instance_method

Rails 3 - Testing controller's GET method - trying to use JSON and getting 406 errors

I have a simple controller that specifies:
respond_to :json
When I try to build a functional test that calls it like this:
test "GET" do
get 'index', :format => :json
end
Everything works just fine. If, however, I try to pass a query parameter in like this:
test "GET" do
get 'index', {:my_param = '1234'}, :format => :json
end
I get a 406 error returned from the controller. If I dump the response via response.inspect, I can see that the #status = 406 and the #header has a content-type of text/html. If I dump the response via response.inspect for the simple case that doesn't pass a query parameter, I see that the #header has a content-type of application/json as I request in the code.
Does anyone have any ideas what I'm doing wrong here? I'm suspecting that I'm screwing up ruby syntax with hashes or something but I'm bumping my head against the wall and not getting anywhere.
I'm on a Mac running Ruby 1.9 and Rails 3.0.5.
Thanks in advance!
This is the function you are calling:
get(action, parameters = nil, session = nil, flash = nil)
The :format part is still a parameter, certainly not a session or flash. Try:
get 'index', {:my_param => '1234', :format => :json}
Oh, important note, use '=>', not '=' ... that's a hash, not an assignment.

How do I authenticate to a Proxy Server from clj-apache-http?

I'm trying to get up an running using http://github.com/rnewman/clj-apache-http
(http/get (java.net.URI. url)
:headers {"User-Agent" user-agent}
:parameters (http/map->params
{:default-proxy (http/http-host :host "localhost"
:port 8888)})
:as :string)
Problem is, my proxy (squid) requires authentication. How do I "feed" my username/password into this library?
Thanks!
Adding the following to my headers dictionary did the trick:
"Proxy-Authorization" (str "Basic "
(base64/encode-str "username:password"))
Like Mac said -- this could also be implemented with a filter -- but preemptive-basic-auth-filter won't work because it sends the headers for WWW-Authorization instead of Proxy-Authorization.
clj-apache-http has a preemptive-basic-auth-filter that you can use. It supports combined username / password strings of this form "name:password". Use of the function is not well documented but can be found here. Example (not tested):
(http/get (java.net.URI. url)
:headers {"User-Agent" user-agent}
:parameters (http/map->params
{:default-proxy (http/http-host :host "localhost"
:port 8888)})
:as :string
:filters ((preemptive-basic-auth-filter "name:password")))

Resources