0

This scenario uses Access-Control-Allow-Credentials alongside the POST method to manage server-side PHP session variables that must remain intact.

For reference, the front-end is a create-react-app project running at http://localhost:3000 and the back-end is PHP running on example.com.

Achieving this with the $.ajax() method is easy and straightforward.

  UseAjax(incomingData) {
    return new Promise(function(resolve, reject) {
      $.ajax({
        url: 'http://example.com/api.php',
        type: 'post',
        data: incomingData,
        xhrFields: { withCredentials: true },
        success: function(data) {
          console.log(data)
        }
      })
      .then((data,status) => {
        // Get the result and transform into valid JSON
        if ( typeof data === typeof 'str' ) {
          try {
            data = JSON.parse(data);
          } catch(e) {
            reject(data,status);
            console.log('Exception: ', e);
            console.log('API Returned non-JSON result: ', data);
          }
        }
        return data;
      }).then((dataObject) => {
        console.log('dataObject:');
        console.log(dataObject);
        resolve(dataObject);
      });
    });
  }

Oddly enough though, when using the fetch() API, it is under the impression that I am not allowing CORS. Of course I have CORS enabled as this request works fine with Ajax and only fails while using the fetch() API.

Here is a look at what I tried while using the fetch() API.

  UseFetch(requestData) {
    return new Promise(function(resolve, reject) {
      console.log('Relay() called with data: ', requestData);
      fetch('http://example.com/api.php', {
        method: 'POST', // or 'PUT'
        body: JSON.stringify(requestData), // data can be `string` or {object}!
        headers: new Headers({
          'Content-Type': 'application/json'
        })
      }).then((result) => {
        // Get the result
        return result.json();
      }).then((jsonResult) => {
        // Do something with the result
        if ( jsonResult.err )
          reject(jsonResult);
        console.log(jsonResult);
        resolve(jsonResult);
      });
    });
  }

It provides this error.

Failed to load http://example.com/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

On the PHP side, I am using a simple output to ensure nothing else is going wrong causing the error on the server's side.

<?php
  header('Content-Type: application/json');
  header('Access-Control-Allow-Origin: http://example.com');
  header('Access-Control-Allow-Methods: POST');
  header('Access-Control-Allow-Headers: Content-Type, Authorization, x-requested-with');
  echo json_encode(['data'=>'result']);
?>

I have followed many questions, but most notably this question with a very thorough explanation of the issue and possible solutions.

For now, I am just using the tried-and-true $.ajax() to complete this task, but I am wanting to fully understand the fetch() API to the extent necessary to replicate this functionality as it seems like something very basic from my experience.

Lux
  • 129
  • 2
  • 12
  • You probably want to use the Network pane of your browser devtools to look at the complete details of the request and response for both cases (the `$.ajax()` case and the `fetch()` case) — including the full request headers and request method and the response headers and the HTTP status code of the response — and then use https://stackoverflow.com/posts/49712690/edit to paste those details into the question. – sideshowbarker Apr 09 '18 at 05:40
  • There is one difference between your $.ajax code and fetch code: In your fetch code, you're adding an additional ContentType header that wasn't being set by the $.ajax code, which would force the browser to send a preflight request. Your php code doesn't seem to be written in such a way that would properly handle a preflight. However.... your error message doesn't quite match that problem. The error message is stating that there was no access control header, which may be due to a PHP error resulting in your standard 500 error page that doesn't have CORS headers. – Kevin B Apr 09 '18 at 17:35

1 Answers1

-2

In your PHP code, you've specified header('Access-Control-Allow-Origin: http://example.com');. This is wrong - you need to specify the value passed in the Origin request header (which in your case would be http://localhost:3000):

header('Access-Control-Allow-Origin: http://localhost:3000');

Ideally you wouldn't hardcode it - you'd just extract the Origin request header and pass that back.

BTW, I believe that JQuery's $.Ajax uses fetch() under the covers (just like XmlHTTPRequest()). Basically, everything uses fetch...

roryhewitt
  • 4,097
  • 3
  • 27
  • 33
  • given the error message received, i don't think this is the current problem, but could potentially be one after the current problem is resolved. The current error message suggests that there is no access control header. If there was one but the origin didn't match, the error message would be different. – Kevin B Apr 09 '18 at 17:41
  • As @sideshowbarker suggests in his comment on your original post, can you provide a complete set of request and response headers? Ah, I see you're not the OP. Apologies. – roryhewitt Apr 09 '18 at 17:46
  • 1
    Your just wrong about jQuery http://api.jquery.com/Types/#jqXHR, it's built on top of XMLHttpRequest, Fetch is a completely new API built at the base level. They are both for handling requests built on TCP/IP using HTTPS or HTTP standards. but that's code built in C++, in using them they are completely isolated things and do not rely on each other and since XMLHttpRequest was the first one. If they were going to build on top they would have built on top of that and not rebuilt XMLHttpRequest to use the fetch API's code. it's just a waste of time. – Barkermn01 Sep 11 '18 at 09:53
  • Martin, you're correct that fetch and XMLHttpRequest are different. I was wrong about that - for some reason I thought XHR had been rebuilt to use fetch. Nevertheless, it seems a bit harsh to downvote my (otherwise possibly correct) answer, no? Even if it doesn't answer the full problem, the fact is that the response headers ARE coded incorrectly. Sigh. – roryhewitt Sep 11 '18 at 23:39