2

I am trying to use an XMLHttpRequest to POST some data to a server (currently localhost, but will later be remote) and retrieve the response. I am using the Cross-origin-resource sharing method described here. However, I never receive the response data. The result is the same if I ignore the cross-origin issue and just send a normal XMLHttpRequest. I have verified using Postman that the URL sent is correct (JSON data is returned). Does anyone see what I am doing wrong here? Thanks very much!

// taken from StackOverflow
function createCORSRequest(method, url){
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr){
        xhr.open(method, url, true);
    } else if (typeof XDomainRequest != "undefined"){
        xhr = new XDomainRequest();
        xhr.open(method, url);
    } else {
        xhr = null;
    }
    return xhr;
}

function sendRequest() {
    var theUrl = "http://localhost:4567/myApi?input="
      + encodeURIComponent("some text input");
    alert(theUrl); // verified this is correct using Postman
    var request = createCORSRequest("POST", theUrl);
    if (request){
        request.onreadystatechange = function() {
          console.log(request); // shows responseText empty
          alert("http returned " + request.responseText);
        };
        request.send();
    }
}

The console.logged request looks like:

XMLHttpRequest {}
onabort: null
onerror: null
onload: null
onloadend: null
onloadstart: null
onprogress: null
onreadystatechange: ()
ontimeout: null
readyState: 4
response: ""
responseText: ""
responseType: ""
responseURL: ""
responseXML: null
status: 0
statusText: ""
timeout: 0
upload: XMLHttpRequestUploadwithCredentials: false
__proto__: XMLHttpRequest
Community
  • 1
  • 1
  • Where does it break down? Open your browser's developer tools. Look in the Console. Are there errors? Look in the Net tab. Do you see the request? Is it formatted as you expect? What about the response? Does it have the right content type? Does it have the right body content? – Quentin Oct 19 '15 at 09:39
  • Thanks for reminding me about the network tab. The POST request says status:"canceled"! And the localhost:4567 is omitted from the "name" column; it just says myApi?input=.... But I did verify that var theUrl is as expected. Any idea if/how this URL is getting truncated before being sent across the network and canceled? – Ellen Sebastian Oct 19 '15 at 09:46
  • Is there an OPTION request before the POST request? If not, how are you triggering the JS? If you send the XHR request immediately before leaving the page, it will get canceled. – Quentin Oct 19 '15 at 09:47
  • @Quentin browsers will send the preflight request automatically. I guess the problem is that the server isn't sending the CORS headers in response to that. – Touffy Oct 19 '15 at 09:48
  • There's no OPTION request - why and how would I send an option request? I'm triggering the request by calling request.send(). I also know that request.onreadystatechange is getting called. I'm not leaving the page at all. How could I prevent the request from getting canceled? – Ellen Sebastian Oct 19 '15 at 09:49
  • @EllenSebastian — The OPTION request will be sent if you make a *complex* cross-origin request. If the browser isn't making one then don't worry about it, that just means you don't have to deal with it on the server. – Quentin Oct 19 '15 at 09:51
  • The problem seems to be the missing data for the `name` in the query string. I can't see any way that it would get lost with this code though. – Quentin Oct 19 '15 at 09:52
  • The server **is** receiving the POST request with the correct data and returning some JSON - even though the request appears as "canceled". It appears, from the server logs, that the JSON is getting returned **after** onreadystatechange is called. So I suppose something is preventing the browser from receiving the server's response. – Ellen Sebastian Oct 19 '15 at 09:54
  • @EllenSebastian most likely it's because your server isn't following the [CORS](http://www.w3.org/TR/cors/) protocol. The browser expects the server to send Accept-Origin, Accept-Methods and other CORS headers in response to the preflight OPTIONS request. Failing that, the browser will cancel the request. – Touffy Oct 19 '15 at 09:59
  • @Touffy — If that was the case there would be an obvious error message in the Console. Also, the OP said there wasn't a preflight options request. – Quentin Oct 19 '15 at 10:00
  • `onreadystatechange` gets changed every time the state changes. So it should get called multiple times over the lifetime of the request and only show the response text in the last version of it. – Quentin Oct 19 '15 at 10:01
  • @Touffy This sounds right. The server has literally nothing other than myApi. Thanks very much! @Quentin, `onreadystatechange` only gets called once, with `readyState==4.` – Ellen Sebastian Oct 19 '15 at 10:02
  • I enabled CORS on the server using [this tutorial](https://yobriefca.se/blog/2014/02/20/spas-and-enabling-cors-in-spark/) and the behavior is the same. It is possible that I did not enable CORS correctly though. – Ellen Sebastian Oct 19 '15 at 10:06
  • Nothing in the console and Network debugger? also, instead of `'*'`, it's better to reflect the request's value in the CORS response headers (i.e. set Access-Control-Allow-Origin to the request's Origin, and so on). Under some circumstances, `'*'` is not allowed. – Touffy Oct 19 '15 at 10:10
  • In the network tab, if I click on the name of my request (`myApi?input=...`), then in the headers tab it shows that the request URL is actually `http://localhost:4567/myApi?input=...`. So the request URL is actually correct. – Ellen Sebastian Oct 19 '15 at 10:10
  • Does your server receive the preflight OPTIONS request? maybe it's ignoring it? I'm not familiar with Spark but it may be that the tutorial you linked is only giving instructions for adding headers to simple GET requests, not responding to OPTIONS. – Touffy Oct 19 '15 at 10:13
  • from what I can tell, the server does not receive any options request. – Ellen Sebastian Oct 19 '15 at 10:16
  • again, the server IS sending a response; the problem is just that the client does not receive it. – Ellen Sebastian Oct 19 '15 at 10:22
  • Does the network tab say anything about sending one, or is the browser sending the POST request directly? can you post the Origin and all Access-Control-Request-* headers sent with the request? – Touffy Oct 19 '15 at 10:22
  • Oh, and are you sending a cookie with the request, and does the server expect a cookie? (if it does, you're screwed) – Touffy Oct 19 '15 at 10:26
  • I am not sure about your first question. I know that the POST request is being sent because the server receives it. In the network tab, it is shown as Canceled. The Origin header is null. I don't see any Access-Control-Request-* header. I am not sending any cookies and the server doesn't expect any. – Ellen Sebastian Oct 19 '15 at 10:26
  • In your browser's network tab, it would look like this (in WebKit): http://tffy.free.fr/network-options.png – Touffy Oct 19 '15 at 10:31
  • Origin is null, huh… are you loading the page from a `file://` URL? – Touffy Oct 19 '15 at 10:33
  • Yes, I am using the Chrome javascript console, and the layout is similar; there are just no Access-Control-Request-* headers: http://imgur.com/leHp1ho The screenshot was taken while the "http returned" alert is shown. – Ellen Sebastian Oct 19 '15 at 10:34
  • Yes, the page is a file:// url: I'm developing locally. – Ellen Sebastian Oct 19 '15 at 10:34
  • Can you try hosting the page on an HTTP server (on a different port than your API)? any server will do. CORS is a bit broken with file:// origins. – Touffy Oct 19 '15 at 10:40
  • will do; installing XAMPP – Ellen Sebastian Oct 19 '15 at 10:43
  • running my html file on localhost using XAMPP, the result is the same as before except `origin==http://localhost`. (no port yet - I'm working on running on a specific port) – Ellen Sebastian Oct 19 '15 at 10:56
  • same result when running on port 8888 except `origin==http://localhost:8888`. Should I try to set the Access-Control-Request-* headers in my javascript XMLHTTPRequest? – Ellen Sebastian Oct 19 '15 at 11:01
  • No, they should be added automatically by the browser when needed, as well as the preflight request. I don't understand why Chrome isn't doing it. – Touffy Oct 19 '15 at 15:27
  • `Access-Control-*` headers are **response** headers. They shouldn't be added by the browser (or put on the request at all). – Quentin Oct 19 '15 at 16:22

0 Answers0