How to pass data with GET request? - clojure

I want to pass form data with GET request.
I have success with this curl:
curl http://sample.com -d 'action=login_request&user_name=balvan'
but I need to pass that same stuff from -d. with this function call:
(http-client/request
{:url base-url
:method :get
:deadlock-guard? false
:insecure? true})
How to add those "-d" parameters within the request body?
I am have [org.httpkit.client :as http-client] in ns declaration and [cheshire "5.8.1"] in project's dependencies.

Issuing man curl tells us that the -d flag will send a post request with data.
-d, --data <data>
(HTTP) Sends the specified data in a POST request to the HTTP
cause curl to pass the data to the server using the content-type
-d, --data is the same as --data-ascii. --data-raw is almost the
ter...
What you want to do is:
(http-client/request
{:url base-url
:method :post
:form-params {"action" "login_request"
"user_name" "balvan"}
:deadlock-guard? false
:insecure? true})

Related

Get data from api using clojure and log result

I'm very new to clojure and I familiar with java and PHP. I have assign new clojure job to get data from outside API and do some process.
This my API request and i tried it using curl command
curl -i -X POST -H "Content-Type: application/json" -d "{\"uid\":\"uId\", \"param\":\"1\"}" http://localhost:8081/api/getData
result is - {"comp":true ,"yUi":"78.2555","xUi":"4.5233","id":"10"}
I have apply above process using clojure and i need log those return values.Could you please help me to do this using clojure
This is how i tried this
(defn- get-data
"api call"
[uId]
(try
(log/info (trs "start process: {0}" (str "uid" uId )))
(when-let [ui (client/post "http://localhost:8081/api/getData" {:form-params {:uid uId,:param "1"} :content-type :json })]
(log/info (trs "get data: {0} {1} {2} {3}" (:comp ui) (:yUi ui) (:xUi ui) (:id ui))))
(catch Throwable e
(log/error "api call failed:" (.getMessage e)))))

wrap-cors middleware not working with system.components

I have the following system.components middleware config, in which I'm using the ring.middleware wrap-cors, to allow for redirects to an external server:
(defn config []
{:http-port (Integer. (or (env :port) 5000))
:middleware [[wrap-defaults api-defaults]
wrap-with-logger
wrap-gzip
ignore-trailing-slash
[wrap-reload {:dir "../../src"}]
[wrap-trace :header :ui]
wrap-params
wrap-keyword-params
wrap-cookies
[wrap-cors :access-control-allow-headers #{"accept"
"accept-encoding"
"accept-language"
"authorization"
"content-type"
"origin"}
:access-control-allow-origin [#"https://some-url"]
:access-control-allow-methods [:delete :get
:patch :post :put]]
]})
And this is supposed to insert headers into every response. But instead, on a request from the client which leads to a redirect to https://some-url, I get the following error in the client browser:
Access to XMLHttpRequest at 'https://someurl' (redirected from 'http://localhost:5000/some-uri') from origin 'http://localhost:5000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Why aren't the correct headers in the response despite adding the middleware?
-- EDIT --
I've also tried the [jumblerg.middleware.cors] wrap-cors middleware like so:
(defn config []
{:http-port (Integer. (or (env :port) 5000))
:middleware [[wrap-defaults api-defaults]
wrap-with-logger
wrap-gzip
ignore-trailing-slash
[wrap-reload {:dir "../../src"}]
[wrap-trace :header :ui]
wrap-params
wrap-keyword-params
wrap-cookies
[wrap-cors #".*"]
]})
And have added the headers using liberator like so:
(defresource some-route [redirect-uri]
:available-media-types ["application/json"]
:allowed-methods [:post]
:post-redirect? true
:as-response (fn [d ctx]
;; added headers
(-> (as-response d ctx)
(assoc-in [:headers "Access-Control-Allow-Origin"] "*")
(assoc-in [:headers "Access-Control-Allow-Headers"] "Content-Type")
)
)
;; redirect uri
:location redirect-uri
)
But still get the ````No 'Access-Control-Allow-Origin' header is present on the requested resource.``` error
Try this library to (wrap-cors):
[jumblerg/ring-cors "2.0.0"]
like this:
(wrap-cors your-routes identity)
Note the third parameter is a function to determine if an origin is allowed (or a list of reg exp)
You might have to add a manual route though:
(OPTIONS "/yourendpoint" req {:headers {"Access-Control-Allow-Headers" "*"}})

Send gzip requests with clj-http

How do I send a gzipped request using the dakrone/clj-http client? So far I have:
(http/post <<REDACTED>>
{:body (->> <<REDACTED>>
cheshire.core/generate-string
.getBytes
clj-http.util/gzip)
:content-type "application/json"
:content-encoding "gzip"
:as :json})
But elasticsearch (the server in my case) is giving 500 errors Illegal character ((CTRL-CHAR, code 31)): only regular white space.
Any ideas?
I guess that you need to enable HTTP compression on the server, e. g. in the Elasticsearch config:
http.compression: true

How can I explicitly set content type on compojure response?

I am playing with compojure-api and am blocked at trying to manage Content-Type for my simple webapp. What I want is to emit an HTTP response that is just plain/text, but somehow Compojure-API keeps setting it to "application/json".
(POST "/echo" []
:new-relic-name "/v1/echo"
:summary "info log the input message and echo it back"
:description nil
:return String
:form-params [message :- String]
(log/infof "/v1/echo message: %s" message)
(let [resp (-> (resp/response message)
(resp/status 200)
(resp/header "Content-Type" "text/plain"))]
(log/infof "response is %s" resp)
resp))
but curl shows the server responded Content-Type:application/json.
$ curl -X POST -i --header 'Content-Type: application/x-www-form-urlencoded' -d 'message=frickin compojure-api' 'http://localhost:8080/v1/echo'
HTTP/1.1 200 OK
Date: Fri, 13 Jan 2017 02:04:47 GMT
Content-Type: application/json; charset=utf-8
x-http-request-id: 669dee08-0c92-4fb4-867f-67ff08d7b72f
x-http-caller-id: UNKNOWN_CALLER
Content-Length: 23
Server: Jetty(9.2.10.v20150310)
My logging shows that the function requested "plain/text", but somehow the framework trumped it.
2017-01-12 18:04:47,581 INFO [qtp789647098-46]kthxbye.v1.api [669dee08-0c92-4fb4-867f-67ff08d7b72f] - response is {:status 200, :headers {"Content-Type" "text/plain"}, :body "frickin compojure-api"}
How do I gain control over Content-Type in a Compojure-API Ring application?
compojure-api serves response in format requested by HTTP client which is indicated using HTTP Accept header.
With curl you need to add:
-H "Accept: text/plain"
You can also provide a list of acceptable formats and the server will serve the response in the first supported format from that list:
-H "Accept: text/plain, text/html, application/xml, application/json, */*"
I never tried compojure so here goes nothing:
1.) your local val reps has the same name as the aliased namespace - kind of confusing
2.) to get access to the params - it seems - you have to apply ring.middleware.params/wrap-params to your routes
3.) ah yes the Content-Type: since you required :form-params, which didn't get delivered due to missing wrap-params you ended up in some sort of default route - hence not text/plain. Thats what I think happend, at least.
with
lein try compojure ring-server
demo/paste into repl:
(require '[compojure.core :refer :all])
(require '[ring.util.response :as resp])
(require '[ring.server.standalone :as server])
(require '[ring.middleware.params :refer [wrap-params]])
(def x
(POST "/echo" [message]
:summary "info log the input message and echo it back"
:description nil
:return String
:form-params [message :- String]
(let [resp (-> (resp/response (str "message: " message))
(resp/status 200)
(resp/header "Content-Type" "text/plain"))]
resp)))
(defroutes app (wrap-params x))
(server/serve app {:port 4042})
test:
curl -X POST -i --header 'Content-Type: application/x-www-form-urlencoded' -d 'message=frickin' 'http://localhost:4042/echo'
HTTP/1.1 200 OK
Date: Fri, 13 Jan 2017 17:32:03 GMT
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 14
Server: Jetty(7.6.13.v20130916)
message: frickin

Compojure app not sending CSRF by default

I'm using reagent and compojure to make a toy webapp and I can't figure out why my server isn't sending out a CSRF cookie. Other answers and several blog posts seem to imply that the default settings for compojure now send the CSRF token and that manually resending it is actually a bug. When I try to hit the POST /art endpoint I get back a 403 Forbidden response. None of the pages get the cookie with the CSRF token in it so I can't send it with the POST request. Any advice?
;;server.clj
(ns my-app.server
(:require [my-app.handler :refer [app]]
[environ.core :refer [env]]
[ring.adapter.jetty :refer [run-jetty]])
(:gen-class))
(defn -main [& args]
(let [port (Integer/parseInt (or (env :port) "3000"))]
(run-jetty app {:port port :join? false})))
;; handler.clj
(ns my-app.handler
(:require [compojure.core :refer [GET POST defroutes]]
[compojure.route :refer [not-found resources]]
[hiccup.page :refer [include-js include-css html5]]
[my-app.middleware :refer [wrap-middleware]]
[environ.core :refer [env]]))
(defroutes routes
(GET "/" [] loading-page)
(GET "/about" [] loading-page)
(GET "/art" [] loading-page)
(POST "/art" request {:sent (:body request) :hello "world"})
(resources "/")
(not-found "Not Found"))
(def app (wrap-middleware #'routes))
;;middleware.clj
(ns stagistry.middleware
(:require [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
[prone.middleware :refer [wrap-exceptions]]
[ring.middleware.reload :refer [wrap-reload]]))
(defn wrap-middleware [handler]
(-> handler
(wrap-defaults site-defaults)
wrap-exceptions
wrap-reload))
I threw the code itself on github here since I still can't see what's wrong.
Other answers and several blog posts seem to imply that the default settings for compojure now send the CSRF token and that manually resending it is actually a bug.
(wrap-defaults site-defaults) applies the ring-anti-forgery middleware. This will only add a CSRF token to each ring browser session and look for the token on POST requests. If the token is missing the middleware will return a 403 for the request. Adding the token to your form or ajax/whatever post requests is up to you, the price of freedom. :)
From the ring-anti-forgery docs:
By default, the token is expected to be in a form field named '__anti-forgery-token', or in the 'X-CSRF-Token' or 'X-XSRF-Token' headers.
For example try adding this route:
(GET "/someform" request (html5 (ring.util.anti-forgery/anti-forgery-field)))
The anti-forgery-field helper will add a hidden input field with the CSRF token as its value, which is picked up by the middleware if the form is posted. To access the token directly you can either use ring.middleware.anti-forgery/*anti-forgery-token* or look it up in the session of the current request map:
(-> request :session :ring.middleware.anti-forgery/anti-forgery-token)
The global var (and by extension the helper) is bound to the handler context though, you can't access it from outside or from another thread in the same context.
Simple curl header example:
Get the CSRF token:
$ curl -v -c /tmp/cookiestore.txt http://localhost:3000/someform
Set the token via header and post some stuff:
$ curl -v -b /tmp/cookiestore.txt --header "X-CSRF-Token: ->token from prev. req<-" -X POST -d '{:foo "bar"}' localhost:3000/art
So I found an answer on a related question that's good enough for me at the moment. I switched the middleware.clj site-defaults to api-defaults which doesn't use CSRF. Still curious how to make CSRF work here, but it's not critical for what I'm doing. If anyone latter suggests a fix that works I'll mark that as correct.