Why sending a POST by AJAX is interpreted by the H

2019-08-12 16:03发布

问题:

I'm testing a HTTP Server that I have developed myself with C++ and Boost libraries. More specifically, I'm testing an endpoint where a JSON is received by PUT.

To test the RESTFul webservice I use Curl with the following command:

curl -H "Content-Type: application/json" -H "Content-Length: 34" -H "Connection: close" -X PUT --data "@response_json" http://localhost:8080/answer

where response_json is a file with the json to be sent. This works fine, the server receives the request as a PUT and do what is supposed to do.

However, when I test the webservice from AJAX with this:

function sendPut2() {
    var http = new XMLHttpRequest();
    var url = 'http://localhost:8080/answer';
    var data = JSON.stringify({"question": "a", "answer": "b"});
    http.open("PUT", url, true);
    http.setRequestHeader("Content-type", "application/json");
    http.setRequestHeader("Content-Length", data.length);
    http.setRequestHeader("Connection", "close");
    http.onreadystatechange = function() { 
        if(http.readyState == 4 && http.status == 200) {
            alert(http.responseText);
        }
    }
    http.send(data);
}

the server receives it as OPTIONS and does not work. Moreover, in Firebug console I can see: "NetworkError: 404 Not Found - http://localhost:8080/answer".

I have tried with Firefox and Chrome. What is wrong in my javascript code?

This is the Firebug with the request from Javascript:

回答1:

The browser has a same origin policy for security reasons. When you request a Ajax PUT in the browser from a different origin than the current web page was loaded from, then the request is subject to that same origin policy. The destination site can choose to support CORS (cross origin resource sharing) which is a specific scheme that the browser implements that lets it ask the target site if a specific cross origin request is OK or not.

Using the OPTIONS request before the PUT request is one such part of the CORS scheme. If the browser detects certain conditions on the original cross origin request, then it will first issue an OPTIONS request and, if it gets the right response from that, then it will issue the target request (a PUT in your case). Things that can trigger the browser to use the OPTIONS request are things like custom headers, certain types of authorization required, certain content types, certain types of requests, etc...

CURL, on the other hand, enforces no such same origin security (that is someting a browser invented for its own web page security model) so it just sends the PUT request right through without requiring the correct answer from the OPTIONS request first.


FYI, if the Javascript in the browser that is making the Ajax request is requesting from the same origin as the loaded web page that contains the Javascript, then it should not trigger the OPTIONS request because it would be a same origin request rather than a cross origin request. If you have a local server, make sure that the web page is being loaded from the local server (same hostname and port number) too, not from the file system and not one using an IP address and the other using localhost or something like that. As far as the browser is concerned, the hostname has to physically be the same, not just the same IP address.


Here's info from MDN on what requests are "preflighted" with the OPTIONS request:

Preflighted requests

Unlike simple requests (discussed above), "preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:

It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted. It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)

FYI, here's a pretty good explanation of the various aspects of CORS. Because your request is a PUT, it will be in the "not-so-simple request" part of that article.