I'm writing a plugin that will intercept all the requests and responses and will extract data and if needed also modify the response. Below is the code I'm using to intercept the request, but it seems I can only read the response and not modify it. The code is injected into the page by manifest.json.
(function(xhr)
{
var XHR = XMLHttpRequest.prototype;
var send = XHR.send;
XHR.send = function(postData)
{
this.addEventListener('load', function()
{
if (postData)
{
if (typeof postData === 'string')
{
try
{
this._requestHeaders = postData;
} catch (err)
{
console.log(err);
}
}
else if (typeof postData === 'object' || typeof postData === 'array' || typeof postData === 'number' || typeof postData === 'boolean')
{
var enc = new TextDecoder("utf-8");
requestdata = enc.decode(postData);
console.log("postData");
var json = JSON.parse(requestdata);
console.log(json);
// Extract data from request
var req = this.responseText
// Change req, this does nothing!
this.responseText = req;
}
}
});
return send.apply(this, arguments);
};
})(XMLHttpRequest);
I understand this is because responseText is actually read only, and then property itself returns a cloned string, rather than a reference to actual response text. Is there any way around it? The only other way I see is using CEF, opening a web site from my CEF application and intercepting the requests there, which is nice, I can enhance the web site inside my application, but on the other hand it's cumbersome and I want my users to be able to download the plugin instead of having to use an exe.
Thanks!
Related
I'm having issue with site. I need to use XMLHttpRequest to send GET request to my api running on Node.js
The Issue is that when I do the request it doesn't send the payload. It's just an empty object. If I do the same without the XMLHttpRequest, for example in Postman everything works.
This is my XMLHttpRequest Function:
// AJAX Client (for RESTful API)
app.client = {}
// Interface for making API calls
app.client.request = function(headers,path,method,queryStringObject,payload,callback){
// Set defaults
headers = typeof(headers) == 'object' && headers !== null ? headers : {};
path = typeof(path) == 'string' ? path : '/';
method = typeof(method) == 'string' && ['POST','GET','PUT','DELETE'].indexOf(method.toUpperCase()) > -1 ? method.toUpperCase() : 'GET';
queryStringObject = typeof(queryStringObject) == 'object' && queryStringObject !== null ? queryStringObject : {};
payload = typeof(payload) == 'object' && payload !== null ? payload : {};
callback = typeof(callback) == 'function' ? callback : false;
// For each query string parameter sent, add it to the path
var requestUrl = path+'?';
var counter = 0;
for(var queryKey in queryStringObject){
if(queryStringObject.hasOwnProperty(queryKey)){
counter++;
// If at least one query string parameter has already been added, preprend new ones with an ampersand
if(counter > 1){
requestUrl+='&';
}
// Add the key and value
requestUrl+=queryKey+'='+queryStringObject[queryKey];
}
}
// Form the http request as a JSON type
var xhr = new XMLHttpRequest();
xhr.open(method, requestUrl, true);
xhr.setRequestHeader("Content-type", "application/json");
// For each header sent, add it to the request
for(var headerKey in headers){
if(headers.hasOwnProperty(headerKey)){
xhr.setRequestHeader(headerKey, headers[headerKey]);
}
}
// If there is a current session token set, add that as a header
if(app.config.sessionToken){
xhr.setRequestHeader("token", app.config.sessionToken.id);
}
// When the request comes back, handle the response
xhr.onreadystatechange = function() {
if(xhr.readyState == XMLHttpRequest.DONE) {
var statusCode = xhr.status;
var responseReturned = xhr.responseText;
// Callback if requested
if(callback){
try{
var parsedResponse = JSON.parse(responseReturned);
callback(statusCode,parsedResponse);
} catch(e){
callback(statusCode,false);
}
}
}
}
// Send the payload as JSON
var payloadString = JSON.stringify(payload);
xhr.send(payloadString);
};
This is code from when I make the request:
app.loadCartViewPage = function(){
var emailAdress = typeof(app.config.sessionToken.emailAdress) == 'string' ? app.config.sessionToken.emailAdress : false;
if(emailAdress){
var queryStringObject = { 'emailAdress' : emailAdress };
app.client.request(undefined, 'api/users', 'GET', queryStringObject, undefined, function(statusCode, responsePayload){
if(statusCode == 200){
var cartId = typeof(responsePayload.carts) == 'object' && responsePayload.carts instanceof Array ? responsePayload.carts[0] : [];
var payload = {'emailAdress' : emailAdress, 'cartId' : cartId };
app.client.request(undefined, 'api/carts', 'GET', undefined, payload, function(statusCode, responsePayload){
console.log(responsePayload);
});
} else {
app.logUserOut();
}
});
} else {
app.logUserOut();
}
};
The first GET request goes through as it should where I pass only queryString, but the second sends just empty objects.
Thanks for help in advance
XMLHttpRequest does not send a body for a GET or HEAD request. Arguments or options for those requests should be in query parameters, not in the body.
Here's a note from the MDN page on .send():
send() accepts an optional parameter which lets you specify the request's body; this is primarily used for requests such as PUT. If the request method is GET or HEAD, the body parameter is ignored and the request body is set to null.
I want to return response data when XMLHttpRequest transaction completes successfully.
upon calling my function trashAnswer() to a variable I expect to assign the value returned to the variable and do something with it like below:
I can see resData is always undefined when i try to access data from it.
Please how can I solve this?
var resData = trashAnswer({answerid:58,answererid:65,questionid:458});
This is the code:
//This function will trash answer object
function trashAnswer(param){
if (typeof param !== 'object' || param == null ) {
throw "trashAnswer(): Strictly expect valid object.";
}
var param = $.extend({answerid:0,answererid:0},param),
fd = new FormData();
fd.append('answerData',JSON.stringify(param));
fd.append('trash-answer',true);
var req = AJAX_REQEUST_OB();
req.open(bigPipe.formMethod.a,ajax.ac,true);
req.onload = function(){
if (req.readyState === req.DONE && req.status === 200) {
//ParseJSON is a custom function to check if response is a valid JSON...if its valid then then function will return response Data else return false
var Data = ParseJSON(req.responseText);
//process data...
var EvalData = !Data ? function(){
throw "Invalid JSON";
}:function(Data){
//do something...
return Data;
};
//call EvalData ****method
EvalData(Data);
}
}
//send request.
req.send(fd);
}
This is an asynchronous call. When you do trashAnswer({...}); you are triggering the request and your code continues to execute. At some point in the future your request may work. Here the "may" part is important: your request may fail for several reasons, but you are only checking the HTTP status 200, that is an "OK" response).
An easy solution:
var resData;
trashAnswer({answerid:58,answererid:65,questionid:458});
//This function will trash answer object
function trashAnswer(param){
// ...
req.onload = function(){
if (req.readyState === req.DONE && req.status === 200) {
// ...
// in your "//do something...":
resData = // your result here. If you do not have a local variable,
// it will change the global one (not recommended, see comment below)
}
}
// ...
}
Your solution is even better if you avoid using a global variable (See here why). But I believe this is a good starting point for you to understand how async calls work.
You can also use promises (including the async/await syntax). A good start reading about how this works is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
How can I edit all POST requests from a client? My research says that it should be possible with a proxy object on XMLHttpRequest. How can I inspect a POST request and edit the form data before it gets sent to the server?
I've tried this approach but the data getting sent through is just responses.
var _XMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function() {
var xhr = new _XMLHttpRequest();
// augment/wrap/modify here
var _open = xhr.open;
xhr.open = function() {
// custom stuff
return _open.apply(this, arguments);
}
return xhr;
}
Here's an IIFE that overloads XMLHttpRequest prototype methods that will allow you to intercept and modify data being sent. I'll leave it up to you to sort out parsing your data
(function(xhr) {
var
proto = xhr.prototype,
_send = proto.send,
_open = proto.open;
// overload open() to access url and request method
proto.open = function() {
// store type and url to use in other methods
this._method = arguments[0];
this._url = arguments[1];
_open.apply(this, arguments);
}
// overload send to intercept data and modify
proto.send = function() {
// using properties stored in open()
if (this._method.toLowerCase() === 'post') {
console.log('USERS DATA :: ', arguments[0]);
console.log('URL :: ', this._url);
// modify data to send
arguments[0] = 'item=beer&id=3';
}
_send.apply(this, arguments);
}
})(XMLHttpRequest);
// use jQuery ajax to demonstrate
$.post('http://httpbin.org/post', { item: 'test', id: 2})
.then(data => console.log('RESPONSE ::', data.form))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I am trying to send a JSON object to a web api, but am having a bit of trouble. Its supposed to take value from an input, check the response first, and if the response is correct, send the data. Here is my JS code:
var form = document.getElementById("inputForm"), master = {}, xhr = new XMLHttpRequest(); //global variables used for checking different parts of the process
form.onsubmit = function (e) {
// stop the regular form submission
e.preventDefault();
// collect the form data while iterating over the inputs
var data = {};
for (var i = 0, ii = form.length; i < ii; ++i) {
var input = form[i];
if (input.name) {
data[input.name] = input.value;
}
master.data = data;
}
// construct an HTTP request
function get(url, callback) {
xhr.open("GET", url);
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
var type = xhr.getResponseHeader("Content-Type");
if (type.indexOf("xml") !== -1 && xhr.responseXML)
callback(xhr.responseXML);
else if (type === "application/json")
callback(JSON.parse(xhr.responseText));
else
callback(xhr.responseText);
}
};
// send the collected data as JSON
console.log(JSON.stringify(master));
xhr.send(JSON.stringify(master));
}
get("http://example.com:12321/data");
};
However, when sending it, I get a HTTP 500 error in the console, and the output in the server itself says:
Processing request on /data
Caught error: <unspecified file>(1): expected object or array
And here is the result of the console.log:
{"data":{"val":"2"}}
I thought I was sending the data correctly, but it isnt recognizing it. The example they gave was of a .json file and that works fine, but my stringified JSON isnt working.
Any help would be greatly appreciated
I need to submit the a form using Ajax with POST method.The code is as follows,
function persistPage(divID,url,method){
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXMLHttpRequest();
xmlRequest.open("POST",url,true);
xmlRequest.onreadystatechange = function(){
alert(xmlRequest.readyState + " :" + xmlRequest.status);
if (xmlRequest.readyState ==4 || xmlRequest.status == 200)
document.getElementById(divID).innerHTML=xmlRequest.responseText;
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);
xmlRequest.send(null);
}
but the form is not submitting(request is not executed or no data posted).How to submit the form using Ajax.
Thanks
There's a few reasons why your code doesn't work. Allow me to break it down and go over the issues one by one. I'll start of with the last (but biggest) problem:
xmlRequest.send(null);
My guess is, you've based your code on a GET example, where the send method is called with null, or even undefined as a parameter (xhr.send()). This is because the url contains the data in a GET request (.php?param1=val1¶m2=val2...). When using post, you're going to have to pass the data to the send method.
But Let's not get ahead of ourselves:
function persistPage(divID,url,method)
{
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXMLHttpRequest();//be advised, older IE's don't support this
xmlRequest.open("POST",url,true);
//Set additional headers:
xmlRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');//marks ajax request
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');//sending form
The first of the two headers is not always a necessity, but it's better to be safe than sorry, IMO. Now, onward:
xmlRequest.onreadystatechange = function()
{
alert(xmlRequest.readyState + " :" + xmlRequest.status);
if (xmlRequest.readyState ==4 || xmlRequest.status == 200)
document.getElementById(divID).innerHTML=xmlRequest.responseText;
};
This code has a number of issues. You're assigning a method to an object, so there's no need to refer to your object using xmlRequest, though technically valid here, this will break once you move the callback function outside the persistPage function. The xmlRequest variable is local to the function's scope, and cannot be accessed outside it. Besides, as I said before, it's a method: this points to the object directlyYour if statement is a bit weird, too: the readystate must be 4, and status == 200, not or. So:
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);//pointless --> ajax is async, so it will alert 0, I think
xmlRequest.send(data);//<-- data goes here
}
How you fill the data is up to you, but make sure the format matches the header: in this case 'content type','x-www-form-urlencode'. Here's a full example of just such a request, it's not exactly a world beater, since I was in the process of ditching jQ in favour of pure JS at the time, but it's serviceable and you might pick up a thing or two. Especially take a closer look at function ajax() definition. In it you'll see a X-browser way to create an xhr-object, and there's a function in there to stringify forms, too
POINTLESS UPDATE:
Just for completeness sake, I'll add a full example:
function getXhr()
{
try
{
return XMLHttpRequest();
}
catch (error)
{
try
{
return new ActiveXObject('Msxml2.XMLHTTP');
}
catch(error)
{
try
{
return new ActiveXObject('Microsoft.XMLHTTP');
}
catch(error)
{
//throw new Error('no Ajax support?');
alert('You have a hopelessly outdated browser');
location.href = 'http://www.mozilla.org/en-US/firefox/';
}
}
}
}
function formalizeObject(form)
{//we'll use this to create our send-data
recursion = recursion || false;
if (typeof form !== 'object')
{
throw new Error('no object provided');
}
var ret = '';
form = form.elements || form;//double check for elements node-list
for (var i=0;i<form.length;i++)
{
if (form[i].type === 'checkbox' || form[i].type === 'radio')
{
if (form[i].checked)
{
ret += (ret.length ? '&' : '') + form[i].name + '=' + form[i].value;
}
continue;
}
ret += (ret.length ? '&' : '') + form[i].name +'='+ form[i].value;
}
return encodeURI(ret);
}
function persistPage(divID,url,method)
{
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXhr();
xmlRequest.open("POST",url,true);
xmlRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);
xmlRequest.send(formalizeObject(document.getElementById('formId').elements));
}
Just for fun: this code, untested, but should work allright. Though, on each request the persistPage will create a new function object and assign it to the onreadystate event of xmlRequest. You could write this code so that you'll only need to create 1 function. I'm not going into my beloved closures right now (I think you have enough on your plate with this), but it's important to know that functions are objects, and have properties, just like everything else:Replace:
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
With this:
//inside persistPage function:
xmlRequest.onreadystatechange = formSubmitSuccess;
formSubmitSuccess.divID = divID;//<== assign property to function
//global scope
function formSubmitSuccess()
{
if (this.readyState === 4 && this.status === 200)
{
console.log(this.responseText);
document.getElementById(formSubmitSuccess.divID).innerHTML = this.responseText;
//^^ uses property, set in persistPAge function
}
}
Don't use this though, as async calls could still be running while you're reassigning the property, causing mayhem. If the id is always going to be the same, you can, though (but closures would be even better, then).
Ok, I'll leave it at that
This code can let you understand. The function SendRequest send the request and build the xmlRequest through the GetXMLHttpRequest function
function SendRequest() {
var xmlRequest = GetXMLHttpRequest(),
if(xmlRequest) {
xmlRequest.open("POST", '/urlToPost', true)
xmlRequest.setRequestHeader("connection", "close");
xmlRequest.onreadystatechange = function() {
if (xmlRequest.status == 200) {
// Success
}
else {
// Some errors occured
}
};
xmlRequest.send(null);
}
}
function GetXMLHttpRequest() {
if (navigator.userAgent.indexOf("MSIE") != (-1)) {
var theClass = "Msxml2.XMLHTTP";
if (navigator.appVersion.indexOf("MSIE 5.5") != (-1)) {
theClass = "Microsoft.XMLHTTP";
}
try {
objectXMLHTTP = new ActivexObject(theClass);
return objectXMLHTTP;
}
catch (e) {
alert("Errore: the Activex will not be executed!");
}
}
else if (navigator.userAgent.indexOf("Mozilla") != (-1)) {
objectXMLHTTP = new XMLHttpRequest();
return objectXMLHTTP;
}
else {
alert("!Browser not supported!");
}
}
take a look at this page. In this line: req.send(postData); post data is an array with values that should be posted to server. You have null there. so nothing is posted. You just call request and send no data. In your case you must collect all values from your form, as XMLHTTPRequest is not something that can simply submit form. You must pass all values with JS:
var postData = {};
postData.value1 = document.getElementById("value1id").value;
...
xmlRequest.send(postData);
Where value1 will be available on server like $_POST['value'] (in PHP)
Also there might be a problem with URL or how you are calling persistPage. persistPage code looks ok to me, but maybe I'm missing something. Also you can take a look if you have no errors in console. Press F12 in any browser and find console tab. In FF you may need to install Firebug extention. Also there you will have Network tab with all requests. Open Firebug/Web Inspector(Chrome)/Developer Toolbar(IE) and check if new request is registered in its network tab after you call persistPage.
I found you've invoked the
xmlRequest.open()
method twice, one with async param as true and the other as false. What exactly do you intend to do?
xmlRequest.open("POST", url, true);
...
xmlRequest.open("POST", url, false);
If you want to send asynchronous request, pls pass the param as true.
And also, to use 'POST' method, you'd better send the request header as suggested by Elias,
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');
Otherwise, you may still get unexpected issues.
If you want a synchronous request, actually you may handle the response directly right after you send the request, just like:
xmlRequest.open("POST", url, false);
xmlRequest.send(postData);
// handle response here
document.getElementById(scriptId).innerHTML = xmlRequest.responseText;
Hope this helps.