What is 'xmlhttp.setRequestHeader();' and in which situations is it used? - javascript

I stumbled on this command while learning AJAX. The guy who made the tutorial didn't explain this command, what do the parameters inside the command mean and what is it used for... Below is the code I used it in:
<script type="text/javascript">
function insert(){
if(window.XMLHttpRequest){
xmlhttp = new XMLHttpRequest();
}else{
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
};
xmlhttp.onreadystatechange = function(){
if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById('message').innerHTML = xmlhttp.responseText;
};
};
parameters = 'insert_text='+document.getElementById('insert_text').value;
xmlhttp.open('POST','ajax_posting_data.php',true);
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xmlhttp.send(parameters);
};
</script>

HTTP is a protocol. Part of that protocol is the concept of request headers. When an xhr happens, text is exchanged between the client and server. Request headers are part of the text that the client sends to the server.
This is a way to set the request headers. The arguments you see are
1) the header to set (in this case, Content-type)
2) the header value. (in this case, x-www-form-urlencoded)
See this for more info.

HTTP requests are messages passed from one computer system to another according to a set routine (a 'protocol' - here HyperText Transfer Protocol) in order to do things like send data, ask for data to be sent back, update data previously sent, etc.
A header is basically a piece of information about the data in the body of the HTTP request. Its purpose is to tell the machine receiving the request what type of data is enclosed in the body of the request, its formatting, the language used, if it's to set a cookie, the date, the host machine, etc.
More than one header can be put on a HTTP request and each header has a 'name' and a 'value' component. On web pages they look like
<meta name="........" content="............."/>
and you find them just below the top of the web page within the element.
To enable people to send HTTP requests from within a JavaScript function, we create a new XMLHttpRequest object, just as your code does so with
const xmlhttp = new XMLHttpRequest();
To this new empty object you intend to add data. Despite its name, XMLHttpRequest also allows sending data in a number of formats other than XML, e.g. HTML code, text, JSON, etc. In your example each data name will be separated from its value by an "=" character and each data/value pairing will be separated from the next pairing by an "&" character. This kind of formatting is known as URL encoding.
We have to tell the receiving computer how the data within the HTTP request body is encoded. There is a standard header to convey this and it is added to the request via the method setRequestHeader(..). This method uses 2 parameters, the header name and the header's value. All this operation is achieved in the line
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
This setRequestHeader(..) method must be applied to the request after the request is characterized with the open(...) method but before the final request is sent off with the send(.) method.
The open(...) method defines: (1) the type of HTTP request, e.g. GET/POST/PUT etc; (2) the web page that contains the handling script for this request, e.g. some .php file or Node.js request endpoint that makes the appropriate query to the back end database; and (3) the nature of the request dynamics, e.g. asynchronous requests are assigned a value 'true', synchronous requests are assigned 'false'.
The send(.) method attaches the data to be sent within the body of the request, in your case the variable called 'parameters'.
On your broader question of which situations setRequestHeader(..) is used, I would say that it is used in most HTTP request situations. But some types of data added to the body of a HTTP request invoke a default setting for the 'Content-Type' header.

It is exactly what it says. It will set a "header" information for the next XMLHttpRequest.
A header is pretty much a key/value pair. It is used to transmit "meta" information to the target server for the ongoing request. In your particular instance, its used to tell the server which content type is used for this request.

It sets the Content-type HTTP header to contain url encoded data sent from a form.

Related

XMLHttpRequest causes two network events

I'm noticing when I have use a XMLHttpRequest in my script, that for each time it gets called two events appear in Chrome's network waterfall.
The first event has a type xhr and a size of around ~27bytes and the second event has a type of text/html and a size of 0bytes.
I've noticed it in the past and have looked over it, but now I'm noticing its causing the page it connects to for it to run twice.
Is this normal? Did I implement something incorrectly?
function example(){
console.log('ran'); // this shows the function only ran once
var form = new FormData(), xml = new XMLHttpRequest();
form.append('test', '...');
xml.upload.onprogress = function(e){something.style.width = (e.loaded*100/e.total) + "%";}
xml.onload = function(){alert('done');}
xml.open('POST', 'https://externaldomainexample.net', true);
xml.send(form);
}
The two requests are being made because you are making a request to a different domain from which your page is hosted (cross origin). If you enable the "method" column in Chrome's network tab you'll see that the first request is an OPTIONS request, whereas the second is your POST request.
The first request is what's known as a Preflight request and asks the remote server what remote hosts are allowed to connect and what methods they are allowed to call. If this step fails, then you'll see an error in the browser saying the request has been "blocked by CORS policy".
If the preflight request is successful, then the second (POST) request is made.
Depending on how the server is configured, you may need to have it handle OPTIONS requests differently, so that it doesn't perform the same action twice.

Download a large file or show error

On a website we are working on, we have a download link, that must be served to the user. However, when fetching the url the server can either serve an error message in JSON (the appropriate headers and an appropriate http status code will then be set) or serve the file.
Currently, we are using an iframe to download this file, but this prevents us from viewing the error message. While, this can be done in principle, it cannot be done cross-domain and the reading the error data seems to be different between browsers (as the browser will interpret the json as html and create html tags around it)
I have considered using xmlhttprequest2 to download the file, and serve it to the user, however the downloaded file can be large and thus, it must be streamed to the user.
Therefore, I'm looking for a way to either download a file or read the error message depending on the http status code.
API Setup
I'm able to change the API to my wishes, however the API is designed to be a public API and is designed as a REST API. This means, that the API should stay as simple as possible, and workarounds to make specific client-side code work should not have cause any hazzle for other client-side (thus the API and client-side code are decoupled).
The file that is being downloaded is encrypted on the server, and can only be decrypted by information given in the URL. Therefore, chunked transfer is difficult, as extracting a chunk would require the server to decrypt the whole file.
the appropriate headers and an appropriate http status code will then be set
If that statement is correct, you could use the same concept as preflighted requests for Cross-site requests. Preflighted requests first send an HTTP request with the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send or not.
In your case, instead of having an OPTIONS request automatically send, you could send manually an HEAD request. The HEAD method is identical to GET except that the server do not return a message-body in the response. The informations contained in the HTTP headers in response to a HEAD request should be identical to the information sent in response to a GET request. Since you're only fetching the headers and not the body, you would have no problem with large or even any file, nothing is downloaded except the headers.
Then, you can read these headers or even the status code, and depending on the result of this manually preflighted request, decide if you should either stream the file to the user or fetch the error message if you're encountering an error.
A basic implementation without knowledge of your project using status code could be the following:
function canDownloadFile(url, callback)
{
var http = new XMLHttpRequest();
http.open('HEAD', url);
http.onreadystatechange = function() {
if (http.readyState === XMLHttpRequest.DONE) {
callback(http.status);
}
};
http.send();
}
var canDownloadCallback = function (statusCode) {
if (statusCode === 200) {
// The HEAD request returned an OK status code, the GET will do the same,
// let's download the file...
} else {
// The HEAD request returned something else, something is wrong,
// let's fetch the error message and maybe display it...
}
}
You can return to iframe JS code which would send message to parent window via postMessage, in this way you could capture errors in host window. There wouldn't be any problems with cross-domain, and content easily would be streamed to browser as it was before.
You could use single request, return response as a Blob, check Blob.type to determine where to utilize FileReader .result to retrieve JSON text from Blob and display error message, or set src of <img> element using URL.createObjectURL
var result = document.querySelector("div");
fetch("/path/to/resource")
.then(response => response.blob())
.then(blob => {
if (blob.type === "application/json") {
var reader = new FileReader();
reader.onload = (e) => {
result.innerHTML = JSON.parse(e.target.result).error
};
reader.readAsText(blob);
} else {
var img = document.createElement("img");
img.src = URL.createObjectURL(blob);
result.appendChild(img);
}
});
plnkr http://plnkr.co/edit/wrzLNm1Sp4k1mfNrYOlQ?p=preview

Auth differentiation between XHR and standard execute

I have the following method
private function adminRequired($accessControl)
{
$user = new \CloseCall\ValueObject\User(2);
if(!$accessControl->isAdmin($user)) {
header("location: /auth");
http_response_code(401);
}
}
Within my controller, this method is always ran as the controller is constructed to check whether a user has admin priveleges or not.
The issue I'm having with this is that XHR requests can also be sent to this controller and a header redirect is not suitable for this, in this case I would want to send a 401 http response back to the javascript and handle this response there.
If I send a header redirect then an http response code then the header redirect is skipped when executing the controller normally.
If I send the response code and then the header redirect then the javascript tries to XHR to my /auth controller.
My question is how can I either differentiate between an XHR request and a standard execute or better yet, handle this gracefully?
By default, there is no way to tell the difference.
Usually, you would add extra headers to the request. The most RESTful way to do that would be to use the Accept header.
e.g.
xhr.setRequestHeader("Accept", "application/json");
… to say that you prefer JSON as the response format.
The server can then test to see if JSON is prefered over HTML and then assume it wants an API style response (rather an HTML response).
A less RESTful and more hacky approach is to use a completely custom header:
xhr.setRequestHeader("x-requested-with", "xmlhttprequest");

AJAX request doesn't send Cookies set from same domain

I'm developing a sort of user-tracking system, that works as follows:
1) A webmaster adds a js script in his website:
<script src="http://example.com/in/tracking.js"></script>
2) When a user loads the page, the javascript request send back a cookie in response:
Set-Cookie:trackid=c1d7fde9cf87a9501cea57cedde97998;Version=1;Comment=;Domain=example.com;Path=/;Max-Age=31556926
(it's basically a simple cookie that lasts for 1 year)
3) The tracking.js makes a POST XMLHttpRequest, to the same example.com domain, passing some parameters:
theAjaxRequest.open("POST","http://example.com/in",true);
theAjaxRequest.setRequestHeader("Content-type", "multipart/form-data");
theAjaxRequest.send(parameters);
4) The backend of example.com should then read the previously set "trackid" cookie, but, instead, I get no cookies on request... By analyzing the POST request via Chrome inspector, I noted that no cookies are passed in request headers (while the first GET request for tracking.js sets correctly the cookie via Set-Cookie).
How come? At first I assumed it may be a problem related to same-origin-policy; so I enabled CORS headers on back-end web server. No results. So, I tried to load tracking.js on a website under same domain of example.com (say web.example.com). Anyway, no results again...
Am I missing something?
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;
invocation.onreadystatechange = handler;
invocation.send();
}
Line 7 shows the flag on XMLHttpRequest that has to be set in order to make the invocation with Cookies, namely the withCredentials boolean value. By default, the invocation is made without Cookies. Since this is a simple GET request, it is not preflighted, but the browser will reject any response that does not have the Access-Control-Allow-Credentials: true header, and not make the response available to the invoking web content.

Requesting specific part of web resource asynchronously

Is there a way of requesting a specific part of a web resource (such as the first 100 bytes) asynchronously from JavaScript?
I'd assumed this could be done through XmlHttpRequest by setting its Range header. However, if the server applies content-encoding (which most do by default), the byte range would apply to the encoded data, not the original. Per HTTP/1.1:
When an entity-body is included with a message, the data type of that
body is determined via the header fields Content-Type and Content-
Encoding. These define a two-layer, ordered encoding model:
entity-body := Content-Encoding( Content-Type( data ) )
Byte range specifications in HTTP apply to the sequence of bytes in
the entity-body […].
This renders the retrieved content useless, since it cannot be decoded without retrieving (and concatenating) the rest of the encoded resource.
It is apparently not possible to disable content-encoding from the client-side, since XMLHttpRequest prohibits the Accept-Encoding from being changed.
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () { /* ... */ };
xhr.open("GET", url, true);
xhr.setRequestHeader("Range", "bytes=0-99");
xhr.setRequestHeader("Accept-Encoding", "identity"); // not allowed
xhr.send();
Is there another way of achieving this (assuming content-encoding cannot be disabled on the server)?
You could try using old style techniques of simulating ajax behaviour like loading hidden iframe with all headers that you mention(and probably the encoding header will be allowed) and getting data from such hidden iframe. Here is example for uploading multipart data using hidden iframe: example. You could change it for your needs. Maybe it will help.

Categories

Resources