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}
Related
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.
Suppose I have an endpoint like so:
get "/foo" do
request.body.rewind
data = request.body.read
# some logic goes here
end
How do I test this in Sinatra?
If I try:
it "gets /foo" do
payload = {:bar => "baz"}
get "/foo", payload
end
the payload gets sent as get parameters and not as request.body.
I am not able to change the endpoint to accept params instead of request.body at this point.
Looking at the code, you can specify it in env with key :input, like so:
get "/foo", {}, {:input => JSON.generate(payload)}
assuming that the input is in json.
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
I am using thin to receive HTTP POST requests, my server code is this:
http_server = proc do |env|
# Want to make response dependent on content
response = "Hello World!"
[200, {"Connection" => "close", "Content-Length" => response.bytesize.to_s}, [response]]
end
Setting a breakpoint, I can see that I have received the content-type (json), and content length, but can't see the actual content. How can I retrieve the content from the request for processing?
You need to use the rack.input entry of the env object. From the Rack Spec:
The input stream is an IO-like object which contains the raw HTTP POST data. When applicable, its external encoding must be “ASCII-8BIT” and it must be opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond to gets, each, read and rewind.
So you can call read on it like this:
http_server = proc do |env|
json_string = env['rack.input'].read
json_string.force_encoding 'utf-8' # since the body has ASCII-8BIT encoding,
# but we know this is json, we can use
# force_encoding to get the right encoding
# parse json_string and do your stuff
response = "Hello World!"
[200, {"Connection" => "close", "Content-Length" => response.bytesize.to_s}, [response]]
end
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.