Let me preface this saying I'm still very new to ClojureScript.
I'm trying to parse an API that has CORS turned off. My only choice is to use JSONP requests. But I have no idea how this is done. As far as I understand JSONP, it's the server that returns a callback script.
How do I implement this in ClojureScript? I have this snippet that I try to use:
(defn get-json [url callback]
(xhr/send url callback "GET"))
(defn logcallback [event]
(let [response (.-target event)]
(.log js/console (response))))
(get-json ("http://www.apiurl.com/method")
logcallback)
But I still receive a Origin "localhost:3449" is not allowed (CORS) error. I guess this is not really a JSONP request. How would I actually do this?
You can use goog.net.Jsonp. Here's a simple example:
(ns jsonp.core
(:import [goog.net Jsonp]
[goog Uri]))
(defn success-handler [res]
(.log js/console res))
(defn error-handler [res]
(js/alert (str "Found an error: " res)))
(let [url "http://en.wikipedia.org/w/api.php?action=opensearch&format=json&search=clojure"
jsonp (goog.net.Jsonp. (Uri. url))]
(.send jsonp nil success-handler error-handler))
If your endpoint returns a "wrapped payload" and the param for setting the callback name is not "callback", you can pass the callback param name as the second parameter to the Jsonp constructor. The following example gets a flickr feed which expects the "jsoncallback" param:
(let [url "http://api.flickr.com/services/feeds/photos_public.gne?format=json"
jsonp (goog.net.Jsonp. (Uri. url) "jsoncallback")]
(.send jsonp nil success-handler error-handler))
goog.net.Jsonp will take care of passing the extra param (jsoncallback in the example) and unwrap the payload by calling the function. In either case it will end calling the success or error handler.
You can take a look into clojurescript 101 by David Nolen. The post is from 2 years ago, but it should still work. There's also another clojurescript tutorial which has a similar example.
Related
I'm attempting to utilize a redirect call nested in an intercept call to point my E2E test to a virtualized service. This has been successful for GET endpoints; however, when calling a POST endpoint the intercepted call continues to be changed to GET. I have attempted to specify the method within the body of the call, but that seems to have had no effect on the call and it continues to try to call a GET resulting in a 405 Method Not Allowed error.
cy.intercept('POST', 'https://realURL.com/endpoint/',
(req) => {
req.method = 'POST'
req.body = {
key: 'ABC123456',
}
req.redirect('http://virtSvc.com/endpoint/')
}).as('Post');
How can I retrieve the body I sent from a xhr (XMLHttpRequest) send(body) call?.
My xhr variable is an XMLHttpRequest ready to call an internal url using the POST method (Ex: /path/api )
xhr.send("a=1");
On the other side, I have implemented a Service Worker and created the handler to catch all fetch requests
self.addEventListener('fetch', function(event, body)
{
event.respondWith( //Check content of event.request.body to run the right action );
}
I can retrieve some properties of the event.request as event.request.url, but I am unable to find the way to retrieve my original xhr body (i.e. "a=1").
Interestingly, when the Service Worker handles this request, and calls the network to get the result,
return fetch(event.request);
the server get access to the body data.
Below an extract of the Request object I receive within the SW fetch method
Request {method: "POST", url: "http://localhost/services", headers: Headers
, referrer: "http://localhost/m1/", referrerPolicy: "no-referrer-when-downgrade"…}
bodyUsed:false
credentials:"include"
headers:Headers
__proto__:Headers
integrity:""
method:"POST"
mode:"cors"
redirect:"follow"
referrer:"http://localhost/path/"
referrerPolicy:"no-referrer-when-downgrade"
url:"http://localhost/path/api"
Any suggestion on how to retrieve the content/body of the send request within the Service Worker fetch() capture?
Thanks!
This is maybe obvious for most people, but I wanted to add some notes with the response, in case someone is in my same situation in the future.
There are several methods to retrieve the body from the request, depending on how the body has been sent
event.request.arrayBuffer()
event.request.blob()
event.request.json()
event.request.text()
event.request.formData()
Any of those methods will return a Promise, which will include the body content. Voila!
I need to thank also Nikhil Marathe (https://hacks.mozilla.org/2015/03/this-api-is-so-fetching/) for helping me understand how all this works.
I am using Friend to build authentication into a Compojure web application.
I have defined a bespoke authentication workflow for Friend:
(defn authentication-workflow []
(routes
(GET "/logout" req
(friend/logout* {:status 200}))
(POST "/login" {{:keys [username password]} :params}
(if-let [user-record (authenticate-user username password)]
(workflows/make-auth user-record {:cemerick.friend/workflow :authorisation-workflow})
{:status 401}))))
The authentication part is factored out:
(defn authenticate-user [username password]
(if-let [user-record (get-user-for-username username)]
(if (creds/bcrypt-verify password (:password user-record))
(dissoc user-record :password))))
This works, but...
I am using AngularJS and having to post request parameters leads to some ugly Angular code (cribbed elsewhere from a StackOverflow answer):
$http({
method: 'POST',
url: '/login',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: {
username: username,
password: password
}
});
I would much rather do this much simpler call instead and just post a JSON object via the request body:
$http.post('/login', {username: username, password: password})
I tried to use ":body" in the authentication handler instead of ":params" but the value of :body seemed neither JSON nor Clojure to me so I don't know how I can use it:
{username me#myapp.com, password password}
I already have JSON request/response mapping workflows working correctly for my REST API handlers, and I checked already that the request headers (e.g. ContentType) were correct for JSON.
So can this be done with Compojure/Friend, and if so how?
Here is some working code and an explanation...
First the Friend workflow, using the request body:
(defn authentication-workflow []
(routes
(GET "/logout" req
(friend/logout* {:status 200}))
(POST "/login" {body :body}
(if-let [user-record (authenticate-user body)]
(workflows/make-auth user-record {:cemerick.friend/workflow :authorisation-workflow})
{:status 401}))))
Second, the authentication function:
(defn authenticate-user [{username "username" password "password"}]
(if-let [user-record (get-user-for-username username)]
(if (creds/bcrypt-verify password (:password user-record))
(dissoc user-record :password))))
Third, the Compojure application with middlewares declared:
(def app
(-> (handler/site
(friend/authenticate app-routes
{:workflows [(authentication-workflow)]}))
(params/wrap-keyword-params)
(json/wrap-json-body)
(json/wrap-json-response {:pretty true})))
Finally a fragment of AngularJS code to post the credentials (username and password come from an AngularJS model):
$http.post('/login', {username: username, password: password});
So what happens is this...
The Angular javascript code posts JSON to the web application login URL. The "Content-Type" header is automatically set to "application/json" and the request body is automatically encoded as JSON, for example:
{"username":"batman#batcave.org","password":"tumblerrocks"}
On the server, the middleware parses the JSON to a Clojure map and presents it to the handler via the ":body" keyword:
{username batman#batcave.org, password tumblerrocks}
The request is then routed to the custom Friend authentication workflow.
Finally the submitted values are extracted from the Clojure map and used to authenticate the user.
I suspect that your wrappers are applied in the wrong order. Check that ring.middleware.json/wrap-json-body is applied before (outside of) the friend wrapper.
e.g.
(def my-handler (wrap-json-body (cemerick.friend/authenticate ...)))
Otherwise, a quick fix might be to just wrap your whole app in ring.middleware.json/wrap-json-params
I am using a script tag proxy like this:
Ext.regModel('login',{fields:['status']});
var loginstore = new Ext.data.Store({ model:'login', proxy:{type:'scripttag',url:'myurl',reader:{type:'json',root:'data'}},autoLoad : true,});
loginstore.load();
In that, the url will return the response format below:
{"data":{"status":"error"}}
I am getting the error:
unexpected token :
Why am I getting this error? What are all the other ways to get the json response from cross domain without callback key and yql.
You cannot use scripttagproxy like that. Please have a look at this thread.
After you configure your server and callback function. You might want to try simpler method to call your cross-domain request as follows.
Ext.util.JSONP.request({
url: some_cross_domain_url,
params: {param1: "something", param2: ...}
callback:function(response){
//response here will be JSON object.
}
});
Please also have a look at this simple tutorial about how to configure your JSONP request.
I would like to know how to obtain the HTTP status code returned after a fetch() operation is performed. I have specified the onComplete and onError callbacks to the fetch() call.
The onError is called in case of an error, but I am unable to obtain the HTTP status code from the parameter passed to onError (it's just the request, not the response).
With XhrGet I was able to get the status code from the ioArgs, and it seems that the JsonRestStore does not handle it that way.
I'm using Dojo 1.5.1 (and I really cannot upgrade yet to 1.6)
The error handler is given two arguments. The second argument (which I called config) has a property called xhr which contains... status and status text.
dojo.xhrGet({
url:'/bogusPath/',
error:function(error, config){
console.log('XHR-ErrorHandle',arguments);
console.log('XHR-ErrorHandle-status:',config.xhr.status);
console.log('XHR-ErrorHandle-statusText:',config.xhr.statusText);
}
})
Returns:
XHR-ErrorHandle [Error, Object]
XHR-ErrorHandle-status: 404
XHR-ErrorHandle-statusText: Not Found