Access-Control-Allow-Origin error but request goes

2019-02-23 08:24发布

问题:

I'm currently deploying a basic API to my live server and I'm running into (what I think is) a CORS problem but there is some behavior going on that I can't explain.

I'm communicating from an AngularJS front-end to a Laravel 5 (+ laravel-cors) back-end.

I started testing with a simple jQuery AJAX call (below) and when I make a request from my local Vagrant environment (http://dev.example.local/test.html) to http://api.example.com/v1/matches I get an error about Access-Control-Allow-Origin. The weird thing is that the request does come through because the information is stored in the database via the Laravel-based API correctly.

$.ajax({
    method: 'POST',
    url: 'http://api.example.com/v1/players',
    data: {
        "username": "username",
        "first_name": "First",
        "last_name": "Last",
        "nickname": ""
    }
}).always(function(r) {
    console.log(r);
});

Error:

XMLHttpRequest cannot load http://api.example.com/v1/players. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://other.example.com' is therefore not allowed access.

The console.log(r) returns {readyState: 0, responseJSON: undefined, status: 0, statusText: "error"}

I developed the application locally using a Homestead VM (API) and a Vagrant environment (application) and it's working correctly within these environments...

Some observations:

  • Each of these requests shows up with Method: POST, Status: 200 OK, Type: xhr in my Chrome Developer Tools.
  • Tools like Postman and PhpStorm's RESTful service tester correctly execute the request and the data is added without errors.

Any ideas on how to further debug this problem are welcome... I've been trying to wrap my head around this for the entire day now and I just don't know what's causing it.

回答1:

Your server must return an appropriate Access-Control-Allow-Origin header in the response. For example, if the request is being sent from http://stackoverflow.com, then your server must return this header: Access-Control-Allow-Origin: http://stackoverflow.com. You can determine, server-side, what the origin is by looking at the Origin header on the request. If your server does not return this header in the response, you will not have any access to the properties of the response browser-side (such as the status code, headers, or message body). The Same Origin Policy is at the center of this restriction.

The reason you are not seeing any similar issues when the request is sent by Postman or PhpStorm's RESTful service tester is due to the fact that these services do not send an Origin header with the request, as they are not subject to the Same Origin policy. By default, the browser will append this header to any cross-origin ajax requests, as browsers are subject to the Same Origin Policy. In my previous scenario, the request header would look like this: Origin: http://stackoverflow.com. Browsers that implement the CORS spec are required to add this request header so the server is able to determine if the origin of the request has been whitelisted for cross-origin ajax requests. If this is the case, the server will return the proper Access-Control-Allow-Origin header. If not, it can simply omit the header. Browsers that do not implement the CORS spec will simply refuse to send such an ajax request.

Regarding your bewilderment as to why the request is being sent in the first place, that comes down to a distinction between "simple" and "non-simple" CORS requests. For simple CORS requests, the request will always be sent to the server, but the client/JS will not be able to parse the response without proper acknowledgement from the server. Some CORS requests are not simple, so to speak. These are, for example, DELETE or PATCH requests, or POST/GET requests that contain non-standard headers (such as X-headers or a Content-Type of "application/json" as opposed to "multipart/form-data"). In other words, a request is not simple if it cannot be sent without JavaScript. For example, a <form> submit, or a GET request from a <script src="..."> will always send "simple" requests. For non-simple requests, the browser must "preflight" the request. This means that the browser sends an intermediate request, called a preflight, before the original request. This preflight request is an OPTIONS request. The server must than return headers in the response to this preflight that acknowledge any non-standard properties of the original request. If it does, then the browser will send the original request.

You can read more about preflighting and CORS in general on MDN.