I have below in routing.yml

sample.post:
  path: '/sample/post'
  defaults:
    _controller: '\Drupal\sample\Controller\SampleController::postData'
  methods: [POST]
  requirements:
    _access: 'TRUE'
    _format: 'json'

In services.yml no change it will be same as default. I don't want to change in service.yml by enabling cors config.

When I call using jQuery.

  jQuery.ajax({
      url: 'http://localhost/sample/post',
      contentType: "application/json",
      type: 'POST',
      complete: function (data) {
        console.log(data);
      }
  });

It gives

Failed to load http://localhost/sample/post: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.

Can I achieve w/o enabling cors config? If I enable cors then it works fine.

up vote 6 down vote accepted

Can I achieve w/o enabling cors config?

No. Without CORS configured, the server and browser will correctly conspire to reject the request.

You could implement it yourself, rather than using services.yml, but something needs to set the headers. And at the point where all you need to do is make a few simple edits to a configuration file, I'd question why you would want to write all that extra code yourself.

  • I totally agree with you. But due to limitations we can't change in services.yml. So is there any alternatives to achieve it? – Ankit Shah Oct 23 at 10:22
  • 2
    Yep, you'll need to implement your own middleware service, or find a way to get your required config to the existing http_middleware.cors middleware without using the services from system.yml. You also need to bear in mind that if the cors.config is missing, Drupal will disable that service (in Drupal\Core\DependencyInjection\Compiler\CorsCompilerPass), so you're always going to be fighting against core. Probably best to implement your own middleware and config completely separate from core's – Clive Oct 23 at 10:39

One workaround I've used in the past, when I've been unable to update the backend/server is to proxy the request through a "repeater" using an AWS API Gateway and a small Lambda function. It's not ideal, but in some cases, like when the API is not yours, or you cannot touch the code, it is necessary.

Obviously, it adds a little overhead/latency and I wouldn't recommend if you can update your service. And it doesn't really work in dev, if you're changing the response... However...

My Lambda function looks like this:

const https = require('https');
const { URL } = require('url');

exports.handler = async (event) => {
  return makeRequest(event).then(body => {
    return JSON.parse(body);
  }, (error) => {
    return { message: error.message, event };
  });
};

function makeRequest(event) {
  return new Promise((resolve, reject) => {
    let responseBody = '';
    const url = new URL(event.url);
    const options = {
      host: url.hostname,
      path: url.pathname,
      port: url.protocol === 'https:' ? 443 : 80,
      protocol: url.protocol,
      method: event.method,
      headers: {
        'Content-Type': 'application/json'
      }
    };
    const req = https.request(options, (res) => {
      res.on('data', (d) => {
        responseBody+=d;
      });
      res.on('end',function(){
        resolve(responseBody);
      });
    });
    req.on('error', (e) => {
      reject(Error(e.message));
    });
    req.write(JSON.stringify(event.data));
    req.end();
  });
}

I don't have any sort of CLI instructions, so here's a run down of the config in AWS web interface. You can configure security in many ways if needed including an API key:

  1. Create a new Lambda function with the name of your choice using Node.js 8 and a new or existing role and entert he above script into the Function Code input area.
  2. Go to the Amazon API Gateway section of the AWS Console and create a New API with the name of your choice.
  3. From the Actions dropdown, select 'Create Method', choose 'POST' and save.
  4. In the resulting endpoint config page, choose 'Lambda Function' as the integration type, leave 'Use Lambda Proxy integration' unchecked, select your Lambda function and save.
  5. From the Actions dropdown, select 'Enable CORS' and then click the button to confirm
  6. Unfortunately, this doesn't add all the CORS headers to the POST endpoint so from the menu on the left, click POST and then click the 'Method Response' box.
  7. Click the toggle on the 200 response and add two more headers: Access-Control-Allow-Headers and Access-Control-Allow-Methods Go back to the Method Execution/POST screen and click the 'Integration Response' box.
  8. Toggle the 200 response and then the Header Mappings and add 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token' to the Access-Control-Allow-Headers header and 'POST,OPTIONS' to the Access-Control-Allow-Methods header.
  9. From the Actions dropdown, select 'Deploy API', select 'New Stage' and give it a name (e.g. 'production' or 'beta').
  10. This will give you an endpoint. Make a POST request to it with the following JSON body and you're done.

    {
        "url": "https://some-non-cors-enabled-api.com/",
        "method": "POST",
        "data": {}
    }
    

GitHub

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.