4

The following request is giving me a 405 Method not allowed when running from a simple create-react-app generated project from localhost:3000. I really need this app to run outside of the domain (decoupled). This feels very much like a cross origin issue but I'm sending the X-CSRF-TOKEN header token and have apache configured to accept the headers as well.

No idea what I'm doing wrong...

Host: contenta.loc
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: GET
Origin: http://localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Access-Control-Request-Headers: authorization,x-csrf-token
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

However, running the same code from within a custom module on the same domain executes as expected.

import React, { Component } from 'react';
import axios from 'axios';
import Querystring from 'query-string';
import SubRequests from 'd8-subrequests';

const URL = 'http://contenta.loc';

class App extends Component {

  state = {
      csrfToken: '',
      oauthToken: {}
  };

  constructor(props){
    super(props);

    this.initializeCsrfToken();
    this.initializeOauthToken();
  }

  initializeCsrfToken(){
    axios.get(URL + '/session/token')
    .then(response => {
      this.setState({csrfToken: response.data});
    })
    .catch((error) => {
      console.log('error ' + error);
    });
  }

  initializeOauthToken(){
    const data = {
      grant_type: 'password',
      client_id: '77e40506-4b2a-4317-b6c0-5ed5b27ce886',
      client_secret: 'test1123',
      username: 'test',
      password: 'test'
    };

    axios.post(URL + '/oauth/token', Querystring.stringify(data))
      .then(response => {
        this.setState({oauthToken: response.data.access_token});
      })
      .catch((error) => {
        console.log('error ' + error);
      });
  }


  fetchSubrequests() {
    const subrequests = new SubRequests(URL + '/subrequests?_format=json');
    const AuthStr = 'Bearer '.concat(this.state.oauthToken);

    subrequests.add({
      uri: '/api/categories'
    });

    subrequests.add({
      uri: '/api/tags'
    });
    subrequests.add({
      uri: '/api/menus'
    });

    axios.get(subrequests.getUrl(), {
      headers: {
        Authorization: AuthStr,
        'X-CSRF-Token': this.state.csrfToken,
      }
    }).then(dataresponse => {
      console.log(dataresponse);
    })

  }

  render = () => {
    return ( 
      <button >
        <div onClick ={
          () => {this.fetchSubrequests()}
        } > Fetch Subrequests</div> 
      </button>
    )
  }

}

export default App;


UPDATE:

Here's what my services.yml cors.config looked like: (THIS IS WRONG???)

   # Configure Cross-Site HTTP requests (CORS).
   # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
   # for more information about the topic in general.
   # Note: By default the configuration is disabled.
  cors.config:
    enabled: true
    # Specify allowed headers, like 'x-allowed-header'.
    allowedHeaders: ['x-csrf-token,authorization,content-type,accept,origin,x-requested-with']
    # Specify allowed request methods, specify ['*'] to allow all possible ones.
    allowedMethods: ['POST, GET, OPTIONS, DELETE, PUT']
    # Configure requests allowed from specific origins.
    allowedOrigins: ['*']
    # Sets the Access-Control-Expose-Headers header.
    exposedHeaders: true
    # Sets the Access-Control-Max-Age header.
    maxAge: false
    # Sets the Access-Control-Allow-Credentials header.
    supportsCredentials: false

This is what my cors.config needs to be for it to work!!

cors.config:
    enabled: true
    # Specify allowed headers, like 'x-allowed-header'.
    # ['x-csrf-token,authorization,content-type,accept,origin,x-requested-with']
    allowedHeaders: ['*']
      # - '*'
    # Specify allowed request methods, specify ['*'] to allow all possible ones.
    # ['POST, GET, OPTIONS, DELETE, PUT']
    allowedMethods: ['*']
    #  - '*'
    # Configure requests allowed from specific origins.
    allowedOrigins: ['*']
    # Sets the Access-Control-Expose-Headers header.
    exposedHeaders: true
    # Sets the Access-Control-Max-Age header.
    maxAge: false
    # Sets the Access-Control-Allow-Credentials header.
    supportsCredentials: false

Here's what my apache.conf looks like:

<VirtualHost *:80>
  ServerName loc
  DocumentRoot /Users/justinwinter/Sites
  VirtualDocumentRoot /Users/justinwinter/Sites/%-2/docroot
  UseCanonicalName Off
  Header set Access-Control-Allow-Origin "http://localhost:3000"
  Header set Access-Control-Allow-Credentials true
  Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, PATCH, DELETE"
  Header always set Access-Control-Allow-Headers: "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization"

  <Directory "/Users/justinwinter/Sites/*/docroot">
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>

  AddType application/x-httpd-php .php
</VirtualHost>

NOT WORKING FROM A SEPARATE JS REACT APP

The following screenshot shows the result of running the same JS from within the contenta.loc domain (same server as the endpoint)

Drupal Custom Module Implementation

WORKING FROM WITHIN A CUSTOM MODULE

The following screenshot shows the error when running the JS from within the create-react-app nodejs localhost server.

405 (Method Not Allowed)

3 Answers 3

2

You can enable CORS support modifying the sites/default/services.yml file

cors.config:
enabled: true
allowedHeaders:
  - '*'
allowedMethods:
  - '*'
allowedOrigins:
   # Note: you need to specify the host + port where your app will run.
  - localhost:8000
exposedHeaders: false
maxAge: false
supportsCredentials: false
2
  • See my cors.config above - forgot to add that before. I I'll try specifying the default host and port though and see if that helps. Funny as I seem to have run into this same thing previously here: drupal.org/node/2715637 Dec 14, 2017 at 0:47
  • Whoa... Seems that specifying the allowedHeaders like this: ['x-csrf-token,authorization,content-type,accept,origin,x-requested-with'] was causing the cors to fail... I don't know how many times I will be bitten by this issue... :( What would production settings look like for allowedHeaders and methods? Dec 14, 2017 at 0:55
2

Maybe try ['x-csrf-token', 'authorization', 'content-type', 'accept', 'origin', 'x-requested-with'] instead.

1

Here is the working example of local.services.yml file which can be placed in the sites/ folder:

# Local development services.
#
# To activate this feature, follow the instructions at the top of the
# 'example.settings.local.php' file, which sits next to this file.
parameters:
  http.response.debug_cacheability_headers: true
  cors.config:
      enabled: true
      # Specify allowed headers, like 'x-allowed-header'.
      allowedHeaders: ['*']
      # Specify allowed request methods, specify ['*'] to allow all possible ones.
      allowedMethods: ['*']
      # Configure requests allowed from specific origins.
      allowedOrigins: ['*']
      # Sets the Access-Control-Expose-Headers header.
      exposedHeaders: false
      # Sets the Access-Control-Max-Age header.
      maxAge: false
      # Sets the Access-Control-Allow-Credentials header.
      supportsCredentials: false
services:
  cache.backend.null:
    class: Drupal\Core\Cache\NullBackendFactory

Check sites/default/default.services.yml for example. Check also: Opt-in CORS support.

The file should be loaded from your settings file, e.g.

/**
 * Enable local development services.
 */
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/local.services.yml';

If you're using Apache, check also: CORS on Apache.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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