I am doing a server request with my firefox sdk, which replies a XML file.
I am parsing the XML file for two special values and put them into a global array (in my onComplete function).
My Problem is that the array does not save the values in element 0 and 1 and I don't know why!
My second Problem is that I want to call a request in my addon for the current tab url (that means more than one time). I know the request module says "Each Request object is designed to be used once" but is it possible to call it more than once?
I always get this error :
Error: This request object has been used already. You must create a new one to make a new request.
var Request = require("request").Request;
var {Cc, Ci} = require("chrome");
var parser = Cc["#mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
var setURL = "example.org";
var exampleArray = new Array();
function parseResponse(response) {
var xml = parser.parseFromString(response.text, "application/xml");
exampleArray[0] = xml.getElementsByTagName("example")[0].getAttribute("example"); // integer value
exampleArray[1] = xml.getElementsByTagName("example2")[0].getAttribute("example2"); //integer value
}
var exampleRequest = Request({
url: "http://www.example.com",
onComplete: parseResponse,
})
exampleRequest.get();
console.log("exampleArray" + exampleArray.length); //always 0
Thanks
Just wrap your request call in a function that's reusable. Request is a constructor, so you can instantiate several requests. But each request can only request one URL.
function request (url) {
Request({
url: url
onComplete: parseResponse,
}).get();
}
For the XML issue, just console log the individual nodes, probably not what you expect somewhere.
Related
I'm trying to figure out how to make a DELETE request using just Javascript. I have a service written in Java Spring where the controller for the url that I am working on has method = RequestMethod.DELETE. My url is, say, http://192.168.50.51/my-service/deleteLocation/{value1}/{value2}/{value3}. In my JavaScript, I have an AJAX function like so:
ajaxFunction : function(url, callback, httpMethod) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
var jsonParse = JSON.parse(xhttp.responseText);
callback(jsonParse);
}
}
xhttp.open(httpMethod, url, true);
xhttp.send();
}
When I want to use the DELETE url, I have an event handler attached to a button that runs this method:
deleteConfirm : function() {
var valuel = this.value1;
var value2 = document.getElementById('element-id').getAttribute('data-element');
var value3 = document.getElementById('element-id').getAttribute('data-element2');
var url = 'http://192.168.50.51/my-service/deleteInfo/' + value1 + '/' + value2 + '/' + value3;
var httpMethod = 'DELETE';
var deleteCallback = function() { alert('deleted!'); }
this.ajaxFunction(url, deleteCallback, httpMethod);
}
However, I keep getting an error in my console: my-javascript.js:59 DELETE http://192.168.50.51/my-service/deleteInfo/123456789/123-456-7AB/12699 406 (Not Acceptable).
I've read that XMLHttpRequest only accepts GET and POST. How do I go about making a delete request using just JavaScript?
Given the information, it looks like your browser is actually making a DELETE request, because the server gave you back a 406 (Not Acceptable) response. It wouldn't do that if your client never sent the request in the first place. This means that the server received your DELETE request and decided it wouldn't process it. So you'll need to look at the server's API to see what gives you HTTP406 and what needs to be different about your request to make it work.
A good way to debug these kinds of things is through your browsers developer tools. Most browsers have a tab in there that shows you the HTTP requests and responses that the browser made. It will make it easier for you to verify these things, going forward.
I am trying to update my kimono API via Google Script in Sheets. There are many urls in the sheet, but I've only shown 2 for this example.
I am receiving HTTP error 404. I've checked, and the apikey and id are fine.
How can I determine what's really wrong?
function PostParameters2() {
var parameters = {
apikey: "--apikey--",
urls: [
"https://twitter.com/search?q=%23running",
"https://twitter.com/search?q=%23swimming"
]
};
var data = JSON.stringify(parameters);
var url = 'https://kimonolabs.com/kimonoapis/--apiId--/update';
var options = {
'method': 'POST',
'content-Type': 'application/json',
'payload': data
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getResponseCode());
}
When debugging external host communication with UrlFetchApp, there are a few tools available to you.
It helps to muteHttpExceptions so you can view them. (In fact, you can simply write your code to handle them yourself, and only throw for exceptions you really don't expect.)
This is done by adding 'muteHttpExceptions' : true to your fetch parameters.
With exceptions muted, fetch won't throw an exception, instead it will pass failure response codes in the HTTPResponse. From there, you can extract the response code and content text for debugging (or automatic handling).
Modify your code like this, to log errors:
var response = UrlFetchApp.fetch(url, options);
var rc = response.getResponseCode();
if (rc !== 200) {
// HTTP Error
Logger.log("Response (%s) %s",
rc,
response.getContentText() );
// Could throw an exception yourself, if appropriate
}
Run, and here's what we see:
[15-08-27 11:18:06:688 EDT] Response (404.0) {
"error": true,
"message": "Could not find API"
}
Some APIs give very rich error messages. This one, not so much. But it does tell us that we have the URL correct, but that the service couldn't find the API we want. Next, dig into why that is so.
We can examine the fetch options and parameters by using getRequest(). Add this line just above the existing fetch() call, and put a breakpoint on the fetch().
var test = UrlFetchApp.getRequest(url, options);
Start the function in the debugger, and when the breakpoint is hit, examine the contents of test carefully.
A common problem is with the encoding of the POST payload. You hand-encoded # to %23 and used JSON.stringify(), so no problem there.
Checking the remaining options, we see that the contentType isn't 'application/json'.
So now you look at your code and find that the name for contentType was mis-typed as content-Type. Remove the hyphen, and try again.
Keep going until you've identified and fixed any other bugs.
Another tip is to use encodeURIComponent() to escape restricted characters in your fetch parameters, rather than hand-encoding them. It simplifies your code, because you can write the "real" characters like # instead of their UTF-8 escape sequences like %23.
Updated code:
function PostParameters2() {
var parameters = {
apikey: "--apikey--",
urls: [
encodeURIComponent("https://twitter.com/search?q=#running"),
encodeURIComponent("https://twitter.com/search?q=#swimming")
]
};
var data = JSON.stringify(parameters);
var url = 'https://kimonolabs.com/kimonoapis/--apiId--/update';
var options = {
'method': 'POST',
'contentType': 'application/json',
'payload': data,
'muteHttpExceptions' : true
};
var test = UrlFetchApp.getRequest(url, options);
var response = UrlFetchApp.fetch(url, options);
var rc = response.getResponseCode();
if (rc !== 200) {
// HTTP Error
Logger.log("Response (%s) %s",
rc,
response.getContentText() );
// Could throw an exception yourself, if appropriate
}
else {
// Successful POST, handle response normally
var responseText = response.getContentText();
Logger.log(responseText);
}
}
I only have to support new browsers.
I have to rely on an external service to provide JSONP data, I do not own that service and it does not allow CORS.
I feel very uneasy having to trust JSONP requests from the external server, since they can run arbitrary code on my end, which would allow them to track my users, and even steal their information.
I was wondering if there was any way to create a JSONP request that is also secure?
(Related: How to reliably secure public JSONP requests? but not with the new browser relaxation)
NOTE: I asked/answered it Q&A style, but I'm very open to other ideas.
Yes!
It is possible. One way to do it would be to use WebWorkers. Code running in WebWorkers has no access to the DOM or other JavaScript code your page is running.
You can create a WebWorker and execute the JSONP request with it, then terminate it when you're done.
The process is something like this:
Create a WebWorker from a blob with the URL to request
Use importScripts to load the JSONP request with a local callback
When that callback executes, post a message back to the script, which in turn will execute the actual callback message with the data.
That way, an attacker would have no information about the DOM.
Here is a sample implementation:
// Creates a secure JSONP request using web workers.
// url - the url to send the request to
// data - the url parameters to send via querystring
// callback - a function to execute when done
function jsonp(url, data, callback) {
//support two parameters
if (typeof callback === "undefined") {
callback = data;
data = {};
}
var getParams = ""; // serialize the GET parameters
for (var i in data) {
getParams += "&" + i + "=" + data[i];
}
//Create a new web worker, the worker posts a message back when the JSONP is done
var blob = new Blob([
"var cb=function(val){postMessage(val)};" +
"importScripts('" + url + "?callback=cb" + getParams + "');"],{ type: "text/javascript" });
var blobURL = window.URL.createObjectURL(blob);
var worker = new Worker(blobURL);
// When you get a message, execute the callback and stop the WebWorker
worker.onmessage = function (e) {
callback(e.data);
worker.terminate();
};
worker.postMessage(getParams); // Send the request
setTimeout(function(){
worker.terminate();//terminate after 10 seconds in any case.
},10000);
};
Here is sample usage that works in JSFiddle:
jsonp("http://jsfiddle.net/echo/jsonp", {
"hello": "world"
}, function (response) {
alert(response.hello);
});
This implementation does not deal with some other issues but it prevents all access to the DOM or the current JavaScript on the page, one can create a safe WebWorker environment.
This should work on IE10+, Chrome, Firefox and Safari as well as mobile browsers.
I'm sending a POST from a chrome extension content script to a server I control. I setup the permissions in the manifest ok. Here is my XHR code. (I want to avoid jQuery for this). Its sending an empty responseText
var xhr = new XMLHttpRequest();
xhr.open("POST",'http://mysite.com/make',true);
xhr.onreadystatechange=function() {
if (xhr.readyState == 4) {
var res = JSON.parse(xhr.responseText);
console.log(res);
}
}
xhr.send({'textbox':data[0].user,'from':'extension'});
data[0].user is an object I got directly from the Twitter API
in my CI controller I have
$user = $this->input->get_post('textbox', TRUE);
$from = $this->input->get_post('from', TRUE);
$fullURL = 'http://www.google.com'; //example of a URL from code.
$json = $this->output->set_content_type('application/json');
$json->set_output(json_encode(array('URL' => $fullURL)));
The response text is empty
a jquery call on the other hand works fine
$.post("http://mysite.com/make", { 'textbox': data[0].user, 'from':'jquery' },
function(data) {
console.log(data);
});
Reason is simple, JQuery post method can accept JSON and then convert it to string and send to the server.
What you are trying to do is to directly send JSON here :
xhr.send({'textbox':data[0].user,'from':'extension'}) // Incorrect way
send method should either accept NULL or a string which is generally made up of QueryString Parameters like.
xhr.send("textbox="+ data[0].user + "&from=extension"); // Correct way
This will ensure that your data goes to the appropriate URL with textbox and from as post request parameters.
and queryString will be generated like textbox=username1234&from=extension in the packet's body unlike one goes in Get with the headers along side the URL.
jQuery's post method makes it simpler for you to format data you send using JSON and then internally it converts that to a queryString to send parameters.
You can't directly send Javascript object like that with an XHR object!
Also checkout this example:
http://beradrian.wordpress.com/2007/07/19/passing-post-parameters-with-ajax/
Using getAllResponseHeaders in the xhr object, is possible to get all the response headers after an ajax call.
But I can't found a way to get the Request headers string, is that possible ?
If this is for debugging purposes then you can just use Firebug or Chrome Developer Tools (and whatever the feature is called in IE) to examine the network traffic from your browser to the server.
An alternative would be to use something like this script:
$.ajax({
url: 'someurl',
headers:{'foo':'bar'},
complete: function() {
alert(this.headers.foo);
}
});
However I think only the headers already defined in headers is available (not sure what happens if the headers are altered (for instance in beforeSend).
You could read a bit more about jQuery ajax at: http://api.jquery.com/jQuery.ajax/
EDIT: If you want to just catch the headers on all calls to setRequestHeader on the XMLHttpRequest then you can just wrapp that method. It's a bit of a hack and of course you would need to ensure that the functions wrapping code below is run before any of the requests take place.
// Reasign the existing setRequestHeader function to
// something else on the XMLHtttpRequest class
XMLHttpRequest.prototype.wrappedSetRequestHeader =
XMLHttpRequest.prototype.setRequestHeader;
// Override the existing setRequestHeader function so that it stores the headers
XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
// Call the wrappedSetRequestHeader function first
// so we get exceptions if we are in an erronous state etc.
this.wrappedSetRequestHeader(header, value);
// Create a headers map if it does not exist
if(!this.headers) {
this.headers = {};
}
// Create a list for the header that if it does not exist
if(!this.headers[header]) {
this.headers[header] = [];
}
// Add the value to the header
this.headers[header].push(value);
}
Now, once the headers have been set on an XMLHttpRequest instance we can get them out by examining xhr.headers e.g.
var xhr = new XMLHttpRequest();
xhr.open('get', 'demo.cgi');
xhr.setRequestHeader('foo','bar');
alert(xhr.headers['foo'][0]); // gives an alert with 'bar'
Something you could to is use Sinon's FakeXMLHttpRequest to replace your browser's XHR. It's described in this document on how to use it for testing but I'm pretty sure you can use the module for your debugging purposes.
What you need to do is:
var requests;
this.xhr = sinon.useFakeXMLHttpRequest();
this.xhr.onCreate = function(xhr) {
requests.push(xhr);
}
And then later on, you can check your requests array for headers by:
console.log(requests[0].requestHeaders);
To access your request headers.